Skip to main content

Full text of "Learning C on the Atari ST"

See other formats


Learning C 

on the 

Atari ST 

Joseph Boyle Wikerl 



i 


Scott, Foresman and Company Computer Books 





























































LEARNING C 
ON THE ATARI ST 

_ J 

Joseph Boyle Wikert 


Scott, Foresman and Company 


Glenview, Illinois 


London 


/ would like to dedicate this book to my parents, Joseph and Bar¬ 
bara Wikert. Thank you for everything you have done and provided for 
me in my life. 

Atari ST and TOS are trademarks of the Atari Corporation 
UNIX is a trademark of Bell Telephone Laboratories 
Lattice is a trademark of Lattice, Inc. 

GEM, AES, and VDI are trademarks of Digital Research Inc. 

Mark Williams C is a trademark of Mark Williams Company 

Library of Congress Cataloging-in-Publication Data 


Wikert, Joseph Boyle. 

Learning C on the Atari ST / Joseph Boyle Wikert. 
p. cm. 

Includes index. 

ISBN 0-673-18738-1 

1. Atari ST computers—Programming. 2. C (Computer program 
language) I. Title. 

QA76.8.A824W55 1988 87-26236 

005.265—del 9 CIP 

Printer’s Key: 1 2 3 4 5 6 EBI 92 91 90 89 88 87 

ISBN 0-b73-lfl?3fi-l 


Copyright © 1988 Scott, Foresman and Company. 
All Rights Reserved. 

Printed in the United States of America. 


Notice of Liability 

The information in this book is distributed on an “As Is" basis, without warranty. Neither the author nor Scott, 
Foresman and Company shall have any liability to customer or any other person or entity with respect to any liability, 
loss, or damage caused or alleged to be caused directly or indirectly by the programs contained herein. This includes, 
but is not limited to, interruption of service, loss of data, loss of business or anticipatory profits, or consequential 
damages from the use of the programs. 

Scott. Foresman Professional Publishing Group books are available for bulk sales at quantity discounts. For infor¬ 
mation, please contact the Marketing Manager, Professional Books. Professional Publishing Group, Scott, Foresman 
and Company, 1900 East Lake Avenue. Glenview, IL 60025. 


Table of Contents 


Chapter 1. What Is C? 1 


The Origin of C 2 
C vs. BASIC and Logo 2 
Atari ST C Compilers 3 

The Graphics Environment Manager (GEM) 4 
Using the Megamax C Compiler 5 
Summing Up 12 
Glossary for Review 12 

Quiz 13 


Chapter 2. C Revealed: Structure and Syntax is 


The Right Stuff: Syntax 16 
The Elements of a C Program 16 

Variables in C 18 
The Basic C Data Types 19 

Int 19 
Float 21 

Characters and Strings 22 
What to Declare? 24 
Assigning Variables 24 
Classes of Storage 25 
Expressing Ourselves 27 
Mathematical Order of Operation 28 
Simple C Arithmetic 29 


Contents 


Program Formatting 31 
Glossary for Review 32 

Quiz 32 


Chapter 3. C Statements 33 


The printf Function 34 

The putchar Function 37 
The scant Function 37 
The getchar Functions 39 

Statements of Choice: The Conditionals 39 
Decisions, Decisions . . . The 
Thinking ST 40 

A Couple of Common Errors with “if” 
Statements 42 
Nesting Your “if” Statements 44 

Logical Operators 46 

The ?: (Conditional) Operator 46 

The Switch Alternative 47 
Quiz 49 


Chapter 4. More Statements: The Looping Structures 


Programs That Repeat 52 

The “while” Statement 52 
Counting Your Loops 54 

Loops That Sum 55 
More Mathematical Operators 57 
The “for” Statement 59 

The “do-while” Statement 65 
“while” vs. “do-while” 66 
Nested Loops 66 

One More Loop: The goto Statement 67 
Glossary for Review 69 

Quiz 69 


vi 


51 


Contents 


Chapter 


Chapter 


Chapter 


. C Functions 71 


Structured Design 72 
What Is a Function? 73 

What Does a Function Consist Of? 73 

Where Is a Function Placed? 73 
How Are Functions Used? 74 
Using Variables with Functions 76 

Global vs. Local Variables 76 

Value Parameters 78 

The Return Statement 80 
Variable Parameters 82 

Glossary for Review 84 

Quiz 84 


. Library Features 85 


How to Avoid Reinventing the Wheel 86 
The Central Library 86 

Standard C Functions 87 
Mathematical 87 
String 88 

Macros 89 
GEM Functions 90 
Windows 93 
Glossary for Review 97 
Quiz 97 


. Programmer’s Corner 99 


The Entire Picture: Complete C Programs 100 
Metric Conversions Program 100 
Guess a Number Program 104 
Decimal to Hexadecimal Conversion Program 
Tape Counter Program 114 
Glossary for Review 117 
Quiz 117 


110 


vii 



Contents 


Chapter 8. Advanced Data Structures and Concepts 


Advanced Numeric Types: Longs, Doubles, Words, 
and Unsigned Types 120 
Simple Arrays 121 

Parallel Arrays 124 

Structures and Files 125 

Using Arrays and Structures in an Application 127 

Recursion (or, Can I Call Myself?) 130 
Glossary for Review 133 

Quiz 133 


Chapter 9. More on Structures and Files 135 


Fields, Structures, and Files 136 

Files vs. Arrays of Structures 137 

File Handling Library Routines 138 

Using a File in a Phone Book Program 139 
Sorting and Merging Entries 143 

Glossary for Review 150 
Quiz 150 


Chapter 10. Debugging and File Analysis 151 


Debugging 152 
File Analysis 153 
Glossary for Review 161 
Quiz 161 


Chapter 11. Graphics 163 


Atari ST Graphics in C 164 

Fun with the Mouse 171 

Glossary for Review 174 

Quiz 174 


VIII 


119 


Contents 


Chapter 12 . A Few Programs for the Road 175 


The Date Minder Program 176 
Batting Average Program 182 
Record Album Data Base Program 187 
Summing Up 195 
Glossary for Review 195 
Quiz 195 


Appendix A: Reserved Words 197 
Appendix B: Answers to Quiz Questions 199 
Index 203 


IX 



Introduction 


As the proud owner of an Atari ST personal computer, you are probably 
already aware of the capabilities of this powerful system. The purpose of this 
book is to allow you to make your computer do what you want through C, 
an equally powerful compiler language. 

For the beginner, the book starts to teach the basics of C on a very low 
level. After a few easy-reading chapters, you will be ready to begin writing 
programs of your own. For the more knowledgeable programmer, special 
care has been taken to point out unique features of the ST, enabling you to 
quickly pick up on this revolutionary computer. Finally, when you are fin¬ 
ished reading Learning C on the Atari ST, you will want to keep it handy as 
a reference guide. 

As you read through the book, you should try out every program that 
is presented. Enter them into your system, and make sure you understand 
them thoroughly. After all, this sort of exercise will usually serve to reinforce 
what you have learned. Good luck, and welcome to the world of C on the 
Atari ST! 



What Is C? 


In this chapter, you will learn: 

• A brief history of the C language. 

• How C compares with other languages available for the ST. 

• What C compilers are available for the ST. 

• What GEM is, and how C interfaces with it. 

• How to enter, compile, and link a short C program. 


J 



Learning C on the ST 


The Origin of C 


The C language was created by Dennis Ritchie in 1972 at Bell Labs. Since 
that time, numerous C compilers have been developed to run on various main¬ 
frames, mini-, and microcomputers. Most of these compilers claim to be a 
“Full K & R C Compiler” as one of their selling points. This simply means 
that their compiler follows the rules of C as laid out in Kemighan and Rit¬ 
chie’s The C Programming Language (1978, Prentice-Hall). This book is 
commonly referred to as the “bible” of C and should be read by anyone who 
is seriously considering writing in C. 

C has become a very popular language over the last several years, mostly 
because of its power and portability. In most high-level languages (for ex¬ 
ample, BASIC, PASCAL), the programmer is limited to how close he can 
get to working with the computer on its own level of bits and bytes. In C, 
however, you have the ability to manipulate memory all the way down to 
the bit level, yet the C code is much more readable than the same instructions 
in assembly language. In addition, because of the Kemighan and Ritchie 
compatibility, a great deal of code written for one machine may easily be 
ported to another with few, if any, changes. This point alone is why so many 
software developers are writing in C; the expense of rewriting a piece of 
software the size of say, an operating system, in assembly language for an¬ 
other completely different computer can be quite overwhelming. Just imagine 
writing a sizable program in BASIC on your Atari ST and trying to make 
“minor” changes to the code so that it will run on your friend’s Apple Mac¬ 
intosh. Finally, well-written C code will generally take on a much more com¬ 
pact and concise appearance than the same code written in most other high- 
level languages. Currently, there are two other popular high-level languages 
available for the ST: BASIC and Logo. 


C vs. BASIC and Logo 


When the Atari ST computers were originally introduced, programmers had 
the option of writing in either BASIC or Logo; interpreters for both these 
languages are included with the machines. BASIC (Beginner’s All-purpose 
Symbolic Instruction Code) is the result of an attempt to develop a language 
that was easier to learn than its predecessors. Developed in 1964 at Dartmouth 


2 


What is C? 


College, BASIC is the most symbolic of the natural languages. Its instruc¬ 
tions are analogous to English-like commands. BASIC has become the most 
popular language among microcomputer users because of its simplistic ap¬ 
proach and ease of use. Logo and BASIC are both used to teach programming 
to the novice, and Logo, with its unique “turtle” graphics, is geared mostly 
to younger programmers. BASIC and Logo, however, have their limitations 
and are not recommended for long, complex programs. 

When you program with an interpreter, the code you entered in is trans¬ 
lated, or interpreted, into the machine’s native language when the program 
is executed. That is, if you type in the following lines using a BASIC inter¬ 
preter: 

10 PRINT "C IS FUN” 

20 GOTO 10 

RUN 

the phrase C IS FUN will be displayed over and over again until you stop 
the program. There was no intermediate step in getting your program to run. 
You simply enter in the statements, and the interpreter acts as somewhat of 
a hidden middleman by specifying your intentions to the computer. 

Using a compiler requires additional step(s), but for the serious pro¬ 
grammer, the time spent is well worth the effort. A compiler translates the 
program into machine-readable form, which may then be stored on a disk or 
linked into a double-clickable application. This action causes most compiler¬ 
generated programs to execute much faster than interpreted ones, since the 
computer no longer needs to translate your statements one at a time while 
the program is running. 

Now that you understand some of the benefits of using a compiler, spe¬ 
cifically C, instead of an interpreter, let’s briefly look at a few of the C 
compilers available for the ST. 


Atari ST C Compilers 


The author has looked closely at the following three C compilers for the Atari 
ST computers: Lattice C (Metacomco), Mark Williams C, and Megamax C. 
Although each of these compilers is fairly easy to use and learn, I recommend 
the C compiler from Megamax because of its ease of use and performance. 


3 



Learning C on the ST 


At the time this book was written, Lattice C was the least expensive of the 
three, and yet the software package itself really has no noticeable shortcom¬ 
ings. One of the major problems with using this C compiler is the lack of 
support from Metacomco, the company that distributes and supports Lattice 
C for the Atari ST. Metacomco used to have a phone number in California 
that users could call for support, but now I understand that you must try and 
call Great Britain for support—not much of a bargain for an inexpensive 
compiler. In addition, it appears that both the Mark Williams and Megamax 
packages are outselling Lattice. 

The Mark Williams package (the second most expensive) is very im¬ 
pressive and contains a 600-page manual that thoroughly explains the com¬ 
piler and most of the standard library routines. My biggest complaint with 
this compiler is the fact that you really need a hard disk to work with it 
without driving yourself crazy by swapping floppies. In addition, this is a 
five-pass compiler, whereas Lattice C requires only two passes, and Mega¬ 
max only one. I also ran into some problems with the msh (microshell) utility 
Mark Williams supplies with the package. One day it would work perfectly 
fine, and the next day I couldn’t compile even the simplest of programs. 

Finally, the Megamax C package is by far the easiest and most note¬ 
worthy C compiler for the Atari ST I have seen. It boasts an editor that makes 
excellent use of the mouse and therefore keeps the user from having to re¬ 
member all the special commands to save, copy, etc. The compile and link 
processes may be easily combined within the development shell, and the com¬ 
piler comes with an excellent 442-page manual that thoroughly describes the 
system. 

For these reasons, all the examples in this book are written using Me¬ 
gamax’s C, but because of C’s portability, very few (if any) modifications 
to them will allow you to run them using a different compiler. Another reason 
why you should be able to execute these programs on different compilers is 
the fact that the Atari uses the Graphics Environment Manager (GEM) to 
handle a great deal of the processing necessary to take advantage of the ST’s 
powerful capabilities. 


The Graphics Environment Manager (GEM) 

As the owner of an Atari ST computer, you are already aware of the graphics 
power this system possesses. The drop-down menus, cute icons, and handy 
windows are only a few of the responsibilities of the Graphics Environment 
Manager. This “manager” is actually a collection of efficient routines that 


What is C? 


were written for the purpose of making all ST applications “look” and “feel” 
the same. Just think how confusing it would be if sometimes the menus came 
from the bottom of the screen up and sometimes from side to side, or if 
sometimes you had to click twice on an icon to open it, whereas other times 
it only took one click. If everyone uses these common routines, all programs 
will operate similarly, and time will be saved. 

The GEM library may be further broken down into the Virtual Device 
Interface (VDI) and the Application Environment System (AES). A descrip¬ 
tion of each GEM routine will be provided as they are used in later chapters. 
A listing of the specific routines available to you with Megamax C is avail¬ 
able in Chapters 11 and 12 of the Megamax manual. The best description of 
GEM and its routines is provided by the Atari ST GEM Programmer's Ref¬ 
erence (published by Abacus Software). This book outlines all of GEM in 
general, but you will still have to familiarize yourself with the interfaces used 
by your specific compiler. 


Using the Megamax C Compiler 


The Megamax C compiler package comes with two 3^-inch microfloppy disks 
called System and Utility. Briefly, the System disk contains the compiler and 
linker and all the supplemental files necessary to compile and link a C pro¬ 
gram, and the Utility disk contains several example programs and a copy of 
the game Megaroids, which, by the way, is quite fun to play. The author’s 
ST configuration includes two disk drives, but the Megamax C compiler may 
be run on single drive systems. You may, however, develop a headache hav¬ 
ing to swap disks unless you can fit your programs on the System disk. For 
you single drive system owners, any reference to drive B simply means you 
should eject the current disk and insert the one referred to in the second drive. 

To start with, you should place your System disk in drive A and a blank 
formatted disk in drive B. This blank disk will be used to hold our C source 
code and executable programs. If you open the window for the System disk, 
you will! see an icon called SHELL.PRG. This icon represents the driver 
program of the Megamax system. If you double-click on it, a screen will be 
displayed with four menus: Desk, File, Execute, and Locate. At this point 
we are primarily interested in the Execute menu, which contains seven items: 
Compiler, Linker, Editor, Disassembler, Librarian, Other, and Make. If you 
select the Editor option, a dialog box is displayed that asks you to select the 


5 




Learning C on the ST 


file you wish to edit. Select Drive B:, and when the disk stops spinning, 
select the New box. Another dialog box will be displayed that asks you to 
enter the name of this new file. Enter the following: 

welcome.c 

and press (RETURN) or click the OK box. The editor will be stalled up. 
Once it has performed all its setup, the cursor will be blinking in the top left- 
hand comer of the screen, and you may begin entering C code. The wel¬ 
comes line that you entered in at the last dialog box means that you wish 
to open, or in this case create, a file on the disk in drive B by the name of 
welcomes. “Welcome” is commonly referred to as the file name and the c 
portion is called the extension. We will always use the c extension for files 
that contain C source code. 

Now go ahead and type in the following short C program: 

/♦First C Program*/ 

#include (stdio.h) 

main {) 

{ 

printf("Welcome to C"); 

getcharl); 

} 

Just enter the program as if you were typing it into a typewriter. However, 
the editor is quite a bit more sophisticated than a typewriter; your mistakes 
can easily be fixed by backspacing over and retyping the incorrect characters. 
There are several menus in the editor that enable you to enter your C code 
easily. These are quite well documented in Chapter 5 of the Megamax man¬ 
ual, and it is not the intention of this book to discuss thoroughly any one 
particular editor. However, most of your code entry can be done simply via 
the mouse (for positioning the cursor) and the backspace key. 

Once you have the program entered, you will want to save it on your 
source code disk. You can do this by selecting the Save option from the File 
menu. The disk in drive B will spin for a moment while the file is being 
saved, and when this process is complete, the editor will be ready for you 
to enter more text. A great feature of the Megamax editor is that you can 
have up to four edit windows open at any one time, so you could be looking 
in different files for routines without having to close one file to get to the 
next. This feature is also very useful when you are fixing compile errors 


6 



What is C? 


because you can open a special file (errors out) that contains all your errors 
and simultaneously have your C source file open for editing. 

If you made any errors while entering the program, fix them now, and 
then use the Save option to update the file. Then, when you have correctly 
entered the code, select the Quit option in the File menu to leave the editor. 
Control now is passed back to the shell, and the previous four menus (Desk, 
File, Execute, and Locate) are displayed. If you were to exit the shell and 
look at the desktop entry for your disk in Drive B:, you would see an icon 
for the welcome.c file. 

At this point, you are ready to compile your first C program. As was 
mentioned earlier in this chapter, the Megamax C compiler is a one-pass 
compiler, which means that your programs must go through only one phase 
of error checking and manipulation before the results are ready to be linked 
and executed. The compiler may be started up by selecting the Compiler 
option from the Execute menu. Again, a dialog box is displayed that requests 
you to enter the name of the file you wish to compile. Select Drive B:, and 
once the entries for that disk are displayed, either click on the welcome.c 
entry and press the OK box or simply double-click on the welcome.c entry. 
The compilation process will then be started, and you should first see a mes¬ 
sage that shows the version number of the compiler and one line specifying 
that the file B:\WELCOME.C is the file being compiled and that the resulting 
output file is B:\VELCOME.o. This latter file is called the object file, and 
it will be used as input for the next phase, linkage. This information is dis¬ 
played, and assuming no errors were encountered, the shell window is dis¬ 
played again very quickly. Again, if you were to look at the desktop entry 
for your disk in Drive B:, you would see an entry for a file called WEL¬ 
COME.O. This file contains information that is used to link your program 
and create the final double-clickable application. Before we discuss linking, 
let’s look at what happens if your code contains errors. 

Let's say that you accidentally entered the program like this: 

/*First C Program*/ 

#include (stdio.h) 

main() 

{ 

printff'Welcome to C") 

getcharO; 

} 

without the semicolon at the end of the printf( “Welcome to C”) statement. 


7 



Learning C on the ST 


If you then try to compile the program, the compiler will abort with the error: 
getcharl); 

"B:\WELCOME.C”, line 7: Function type expected before '(' 

Number of errors: 1 
press RETURN to continue 

The line number 7 refers to the line number in your source code. If you had 
a different number of blank lines in your file, your number may be different. 
After the line number, the error mnemonic is displayed. Although this error 
doesn’t specifically say that you forgot to put a semicolon at the end of your 
printf statement, if you go back and look at your source code, it should be 
apparent why the compiler got confused. To fix this problem, simply press 
RETURN, and the Megamax shell will return to your file in the editor. In 
addition, another window with the file A:\ERRORS.OUT is open. As was 
discussed above, this file contains the error messages encountered in your 
last compile. You may either select your source code file (by clicking any¬ 
where in it) or close the ERRORS.OUT file by clicking on its close box in 
the upper left-hand comer. Now go ahead and put the semicolon back on the 
end of the printf(“ Welcome to C”) line, then update and exit the editor via 
the Save and Quit options in the File menu. When you have returned to the 
shell, run the compiler (Compiler option in the Execute menu) again on the 
corrected version of your program so that a valid WELCOME.o file will be 
generated for the linker. 

Now that we have an error-free WELCOME.o file, the next step in 
creating a double-clickable application is to link this file with some other files 
to tie up some loose ends. Our program is quite simple; it will merely display 
the message Welcome to C and wait for you to press a key to continue. Let’s 
take a closer look at what exactly will cause these actions to take place in 
the code: 

/•First C Program*/ 

#include (stdio.h) 
main(} 

{ 

printff'Welcome to C"); 
getchar(); 

} 

The first line of the program is enclosed in the characters /* */. These char- 


8 


What is C? 


acters designate a comment, and the compiler ignores everything between 
them. Comments are very important ways of leaving notes in your code so 
that you or someone else will know exactly what you were trying to do when 
you wrote it. The next line in the code instructs the compiler to pull in the 
file named stdio.h (the .h extension is used to specify this file as a “header” 
file) via the include directive. Quite simply, when your code is being com¬ 
piled, if a #include is encountered, the compiler treats your program as though 
the entire file specified was typed in at that spot. If you look at the contents 
of the stdio.h file on your System disk, you’ll see it contains a lot of infor¬ 
mation that, although it may be meaningless to you now, is used to assist 
the compiler in translating various input/output (I/O) instructions. Input/ 
output instructions are those instructions that allow the computer to accept 
the information you enter into it and provide information back to you via the 
screen, speaker, etc. You will see this directive used throughout this book 
and in most other C programs because 1) it is much easier than typing in the 
information contained within that file every time you write a program that 
uses I/O, and 2) even if you did want to have that information in every one 
of your C source files, if for some reason the stdio.h file needs to be changed, 
you would have to change it in every single program you entered it into! 

As you can see, the #include directive is a very useful shortcut to ref¬ 
erence common external files in your C code without having to worry about 
entering them in and keeping up with modifications to them. 

The next statement in the program is main( ). This statement says that 
this is the start of a routine or function, and this is where the actual program 
instructions begin. A function performs a simple task and is designated by 
a set of instructions enclosed within the symbols { and }. This particular pro¬ 
gram has only one function called main, but you will later see programs that 
have numerous other functions used to perform different tasks. Because a 
program can, and usually does, contain multiple functions, the compiler needs 
to have some method of determining where to start processing instructions. 
For this reason, main is reserved to designate the beginning of the central 
routine. 

The next statement, 

printf(''Welcome to C"); 

is responsible for displaying the message “Welcome to C” on your screen. 
Printf is an external function that takes parameters (within the parentheses) 
and, based on certain formatting rules that will be discussed later, sends char¬ 
acters to the monitor. Note the use of the semicolon (;) at the end of this 


9 



Learning C on the ST 


statement. The compiler must have a method of knowing where each instruc¬ 
tion ends and the next one begins, and the semicolon is used for this purpose. 

Finally, the last statement enclosed within the main function’s start ({) 
and end (}) symbols is 

getchar(); 

which is responsible for making the program wait until the user presses the 
RETURN key on the ST’s keyboard. Getchar is another external function, 
and, because of this I/O, we generally need to place the #include (stdio.h) 
statement at the beginning of our program. However, the stdio.h file does 
not contain the actual instructions that cause the I/O to work; these instruc¬ 
tions are in another compiled file, and the only way we can have them in¬ 
cluded in our simple program is to use the linker to call them in. This com¬ 
bining of files is performed by the linker, which uses your instructions to tell 
it exactly which files to work with. It is not important for you to know a 
great deal about how the linker works, but you should be aware that you can 
use it to pull together several of your own compiled files as well as other 
compiled files that come with the Megamax system to create your executable 
program. Even if you feel you don’t need to call in other external files, it is 
still necessary to put your object file through the linker because the compiler 
itself does not output a double-clickable application. 

With this knowledge about linking, start up the linker by selecting the 
Linker option from the Execute menu. A new dialog box will be displayed 
that asks you to keep entering the names of the files you wish to have linked. 
Select Drive B:, and click on the WELCOME.O entry. Then click the ))ADD}) 
box, and you should see the entry for B:\WELCOME.O in the “To be linked” 
miniwindow. If we had other files to pull into our link, we could continue 
to select them in this manner. Now use the keyboard to change the name of 
the Executable File (at the bottom of the dialog box) from A.PRG to WEL- 
COME.TOS. Finally, click on the OK button, and the link process will be 
started. 

When the linker starts up, the screen is cleared and information about 
the linker is displayed. The linker then informs you that your WELCOME.O 
file is being loaded, and then all the external files are traversed to determine 
if you need any of the information in them. When all the external files have 
been looked at, some information about the sizes of different segments of 
your program is displayed, and finally, control is returned to the shell, if no 
linkage problems are encountered. If an error occurred during linkage, an 


What is C? 


error message will be displayed, and you should correct the problem and 
relink. 

Atter a successful link, you should see an icon named WEL¬ 
COME.TOS on your B disk if you return to the GEM desktop. There’s no 
need to leave the shell to check this, though; simply select the Other option 
from the Execute menu. In the resulting dialog box, you should see an entry 
for WELCOME.TOS. Now go ahead and double-click on the WEL¬ 
COME.TOS entry. The screen is cleared, the mouse is hidden, and within 
a few seconds the message Welcome to C is displayed on your screen. The 
message will remain on your screen until you press RETURN. When RE¬ 
TURN is pressed, the program is terminated, and control is returned to the 
Megamax shell. 

An easier method of compiling and linking your programs is provided 
by Megamax via the “make” utility. Before using “make,” however, you 
must always make sure that system time and date on your ST are current. 
This may be achieved by using the Control Panel option in the Desk menu. 
Once you have these items properly set, enter the editor, and create a file 
called WELCOME.MAK on the disk in drive B. Enter these lines in the 
file: 


stdio.h 

b:welcome.c 

mmlink.ttpb:welcome.o-ob:welcome.tos 

The first line tells “make” that you wish to pull in the stdio.h file when 
compiling. The second line is used to inform the utility that you want to 
compile the file welcome. c on the disk in drive B. Finally, the last line tells 
“make” that you want to link b:welcome.o, with the resulting executable file 
being b:welcome.tos. Notice that your executable program will now be placed 
on the disk in drive B; this way you can keep your System disk free of un¬ 
wanted files and keep all your own files on the disk in drive B. Save off the 
File and open your source code file brwelcome.c. The “make” utility checks 
to see if your source code file has been updated more recently than its as¬ 
sociated object file (b:welcome.o). If it has been, “make” knows that the 
file should be recompiled. So even though we have no changes to make to 
b:welcome.c, we must change the time entry for it on the disk so that “make” 
will recompile it for us. Simply Save and Quit the editor, and select the Make 
File option from the Locate menu. Select the WELCOME.MAK file from 
drive B and click on the OK button. By doing this, you have informed the 


11 


Learning C on the ST 


Megamax shell that you want B:\VELCOME.MAK to be the information 
file used in the “make” utility. Now select the Make option from the Execute 
menu, and the compile/link process will be automatically done for you! 

Congratulations, you have just finished compiling and linking your first 
C program on the Atari ST! Go back and review any of the areas that may 
have been unclear to you on your first reading, and be sure you understand 
all the concepts presented in this chapter, because they will be the foundation 
for the remainder of the book. 


Summing Up 


This chapter has presented you with some basic information on the C lan¬ 
guage, including a history and comparison of it with the other popular lan¬ 
guages available on your Atari ST. A brief discussion of the C compilers 
available for the ST and the reasons why this book is based on Megamax’s 
product were also discussed. Finally, you have worked with the editor, en¬ 
tered the source code for your first C program, compiled, linked, and exe¬ 
cuted it, and have been introduced to some of the more fundamental areas 
of C (comments, the #include directive, functions, statements, etc.) as well 
as Megamax’s powerful “make” utility. 


Glossary for Review 

interpreter —a translation program that converts your code into the ma¬ 
chine’s native language when the program is executed 

compiler —a translation program that converts your code into machine- 
readable form, which may then be stored on a disk or linked into an ap¬ 
plication 

GEM —Graphics Environment Manager 

VDI —Virtual Device Interface 

AES —Application Environment System 

linker —pulls together one or more compiled files (.o) and creates a 
double-clickable program 


What is C? 


Quiz 


1. What does it mean when a compiler is said to be a “full Kemighan and 
Ritchie C compiler’'? 

2. What are the two fundamental reasons for C’s popularity? 

3. How does a C compiler translate this line? 

/*prigf{"Welcome to C");*/ 


13 








C Revealed: Structure and 

Syntax 


In this chapter, you will learn: 

• What the proper structure and syntax of a C program is. 

• What the different data types are. 

• What a variable is. 

• How to use comments and # define constants. 

• How to use mathematical operations for arithmetic. 


Learning C on the ST 


This chapter focuses on many programming fundamentals necessary to 
use C on the Atari ST. The first step in the programming process is to analyze 
the problem and then to develop a general solution or algorithm. Next, the 
solution is translated into C language instructions and then implemented to 
test the results. From now on, we will emphasize the task of writing the 
program instruction developed from algorithms. 

Before we can begin to solve complex problems, we must first master 
some fundamental concepts of the language itself. The first concept is the 
structure of a C program and all the elements that make up a simple program. 
Later in this chapter, we will introduce the concept of a variable and show 
how this feature is invaluable for more complex programming problems. 


The Right Stuff: Syntax 


When working with any language, certain rules must be followed. A natural 
language, such as English, calls these rules grammar. A programming lan¬ 
guage, like C, refers to these rules as syntax. Writing a program in C requires 
that the program be properly constructed. Strict rules are imposed when writ¬ 
ing programs. Programs must be syntactically correct and use valid state¬ 
ments. If the rules or syntax are not followed—for example, if a semicolon 
is misplaced or a key word is missing—a program will fail, leaving the pro¬ 
gramming problem unsolved. Throughout this book, we will learn many syn¬ 
tax rules. In the next section, we introduce a few of the most common ones. 


The Elements of a C Program 


In general, a C program may be broken up into the following elements (in 
pseudo-C code): 

#include files 
#define constants 
global structures 
global variables 
main() 

{ 


16 




C Revealed: Structure and Syntax 


main structures 
main variables 
main code 
} 

funct() 

{ 

fund structures 
fund variables 

fund code 

} 

func2() 

{ 

func2 structures 
func2 variables 

func2 code 

} 

funcN() 

{ 

funcN structures 
funcN variables 

funcN code 

} 

We saw an example of an #include file in Chapter 1 with #include 
(stdio.h). There are, of course, other files that may be pulled into your C 
source code with the #include directive. Any time you see the #include 
directive being used in a program, it simply means that the program needs 
to reference at least one item specified in that file. As a final note on in¬ 
clude, the file name may appear in double quotes (“filename.h”) instead of 
the brackets ((filename.h)). Both methods are correct, but you should choose 
one and use it always. 

The ability to set up symbolic constants in your C programs exists via 
the #define initiative. If you are writing a program that asks users to enter 
a number, and if it matches your lucky number, they win, you might want 
to declare your lucky number like this: 

#define LUCKY 13 


17 


Learning C on the ST __ 

When your program is compiled, anywhere the characters LUCKY appear 
(except in quotes), the compiler will replace them with the number 13. In 
this manner, you can change your lucky number by simply changing its def¬ 
inition in the #define statement and not have to worry about changing every 
occurrence of it in your code. The name LUCKY is called an identifier be¬ 
cause it identifies the number 13 as a constant that may be used throughout 
your program. 

C places some restrictions on identifiers. For instance, identifiers may 
only contain upper- or lowercase letters, digits, or the underscore (_) symbol. 
More specifically, they must begin with a letter, and although they may be 
several characters in length in Megamax C, only the first ten characters are 
actually used by the compiler. For this reason, abcdefghijk and abcdefghijl 
would be considered the same name even though you and I realize the dif¬ 
ference. In addition, C programmers have conventionally used uppercase 
characters for #define identifiers and lowercase characters for variable dec¬ 
laration identifiers (which we will discuss shortly). This naming convention 
makes it easy for a programmer to know immediately whether the name iden¬ 
tifies a constant or a variable. You should also note that there is no semicolon 
used at the end of a #defirie statement. 

Both the #include and #define directives are part of the C preproces¬ 
sor, which performs various tasks before actually compiling your C program. 

The next parts of a C program are the global structures and global vari¬ 
ables. In fact, as you can see, the remainder of our C program structure is 
nothing more than various functions with their appropriate structures and vari¬ 
ables. We’ll discuss structures shortly, but first let’s discuss the concept of 
variables. 


Variables in C 


What is a variable? You may recall from basic algebra that a variable is 
something that you can change. Computers use variables in much the same 
way: A variable represents something that can change. Specifically, data val¬ 
ues are stored in memory at different locations. Each location may contain 
a value that can change. The computer must have the ability to manipulate 
data and change its values. 

In algebra, a common expression might be represented by v — x + -2. 
The value of y changes as the value of x changes, a: and y are called variables 
because you can change their values. Similarly, visualize an empty box marked 


18 


C Revealed: Structure and Syntax 


X. This box can represent a location in memory, and it can only accept one 
item at a time. You can put any value in this box you wish, as if it were a 
constant value, until you change or remove it. 

On the Atari ST, each individual box is represented by a numeric ad¬ 
dress or location. The 520 ST contains approximately 524,000 of these lo¬ 
cations in RAM, whereas the 1040 ST contains approximately 1,048,000. 
Fortunately, C allows us to assign a descriptive name (an identifier) to these 
locations. Using identifiers to represent specific locations in memory is much 
easier than using thousands of unique numeric addresses. Each particular 
identifier must have a type associated with it in order for C to understand 
how you intend to use it. So let’s look at the basic data types available to 
you in C. 


The Basic C Data Types 


In programming, data is represented using different types of information, such 
as letters, numbers, and sets of characters. Before these types of information 
can be processed, they first must be defined. In this section, we review the 
common data types required for short programs. These data types include int, 
float, and char. 

Int 

An int (short for integer) is a whole number, which may be either positive 
or negative, that cannot include a comma. Signs—positive ( + ) and negative 
(-)—are allowed. If a positive sign is omitted, the positive is assumed. As 
you know, the number of integers is infinite. However, there is a limit to the 
number available on the ST in Megamax C. The largest number represented 
by an int type is 32767, while the smallest is —32768. In any of our variable 
sections listed above, we could insert the line: 

int a; 

and we would then have a space in memory set aside that could be referenced 
by the variable name a and could contain any of the values between and 
including 32767 and —32768. An int requires 16 bits of storage space in 
Megamax C and therefore is two bytes long. A byte is simply a group of 8 


19 


Learning C on the ST 


bits, or binary digits, which is used to hold data on a computer in the base 
2, or binary, number system. For those of you unfamiliar with binary math, 
the following is a brief table of decimal numbers and their binary equivalent: 

Decimal Binary 

1 00000001 

2 00000010 

3 00000011 

4 00000100 

5 00000101 

It is not necessary that you thoroughly understand the binary numbering 
system now, but if you are unfamiliar with it, it is suggested that you read 
one of the numerous mathematics books that are dedicated to this system. 

There are other data types that may be used to define an integer besides 
the int. If you do not need the full two bytes of storage for your variable, 
you could declare a to be a short int like this: 

short int a; 

or, more simply: 

short a; 

Either method will define a variable named a that occupies one byte or 8 bits 
in memory, half the size of a regular int. Alternatively, we could define a 
as a long int like this: 

long int a; 

or, again leaving off the int descriptor: 
long a; 

The long int in Megamax C on the ST defines a storage location of 32 bits 
or 4 bytes, twice the size of the normal int declaration. 

Another modifier, unsigned, may be used to alter the range of values 
allowed in your int variables. An unsigned short, declared like this: 


20 



C Revealed: Structure and Syntax 


unsigned short a; 

still allocates one byte of storage, but now the range of allowable values in 
a is 0 to 255. An unsigned int may be declared like this: 


unsigned a; 

which results in the allocation of 16 bits of storage in which any of the values 
0 to 65535 may reside. An unsigned long int may be declared like this: 

unsigned long a; 


which results in the allocation of 32 bits of storage in which any of the values 
0 to 4294967295 may reside. In summary. Megamax C's various int data 
types on the ST are: 


Data Type Size in Bytes 


Range 


int 2 

long int 4 

unsigned int 2 

unsigned long int 4 

short int 1 

unsigned short int 1 


-32768 to 32767 
-2147483648 to 2147483647 
0 to 65535 
0 to 4294967295 
-128 to 127 
0 to 255 


Notice that the use of the unsigned modifier does not affect the storage 
size allocation, but does affect the range of values that may occupy that space. 
As you know, mathematical operations are not limited to whole number val¬ 
ues. For this reason, the float data type may be used to work with real num¬ 
bers. 


Float 

Real numbers are numbers that contain decimal numbers. Generally, at least 
one digit is placed to the right of the decimal point, indicating the fractional 
part of the real number. In C, real numbers are called floats because of their 
floating decimal point, which is not restricted to any particular position. Floats 
are commonly referenced in what is called scientific or exponential notation. 
Scientific notation is the method the ST uses to work with very large 


21 


Learning C on the ST 


or very small numbers. For example, the number 5 million, represented as 
5.0 x 10 7 , is written as: 

5.0E + 7 

where the number 5.0 is called the coefficient or mantissa, and the number 
+ 7 is called the exponent. The 5 is the whole part of the number to the left 
of the decimal point, while the fractional part, or exponent, represents the 
power of ten or the number of places to the right of the decimal point. The 
letter “E" means exponent. Therefore, to determine a number written in sci¬ 
entific notation, move the decimal point of the mantissa the number of places 
specified by the exponent to the right. Another example: 4.43E + 2 repre¬ 
sents 443. 

Similarly, very small numbers are represented with scientific notation. 
For example, the real number represented by .00001234 is written as: 

1.234E - 5 

where the negative exponent means that the decimal point of the mantissa 
should be moved five spaces to the left instead of to the right. For a negative 
real number, the negation sign is placed in front of the mantissa; that is, 
-4.52E - 3 represents -.00452. 

In Megamax C, the data types float and double, or long float, are avail¬ 
able for variable definition. The declaration 

float a; 

defines a variable space that is 32 bits long and may hold any of the values 
from +/—10E—37 to + /-10E38. A double float is declared like this: 

double a; 

in which a represents a float that is 64 bits long and may be in the range of 
+ /-10E—307 to + /-10E308. 


22 


Characters and Strings 

Fortunately, numbers are not the only data types that can be manipulated with 
C. We can also work with text information that is represented by upper- and 



C Revealed: Structure and Syntax 


lowercase letters, special characters, etc. Such information is represented by 
the data type char. A char variable may be declared like this: 

char a; 

which would result in the allocation of one byte of memory for a character 
value. Thus, a could hold the value ‘M\ or ‘8’, or ‘#\ etc. When we show 
a character value, it is always represented with that character surrounded by 
single quotes (‘ ’). At this point you can see that the char data type is quite 
useful. But what makes it even more powerful is the fact that it can be used 
to define variables that hold more than one character—called strings. A string 
is nothing more than a collection of characters with a known end point. We 
set up a char variable to hold more than one character by making it an array 
of characters. (The concept of arrays is thoroughly discussed in Chapter 8; 
however, a brief introduction to them is necessary for you to understand strings 
in C.) For instance, the following declaration 

char a(10]; 

would allocate space in memory to hold a string of up to ten characters. The 
name “Oliver” could be put into this string, as could the name “Atari.” When 
we show the value of a string, it is shown surrounded by double quotes (“ ”). 
If you were to look into the actual memory location occupied by the variable 
a, you would see the following when “Oliver” is in it: 


‘0"l"i"v"e"r' '\0' 


and this when “Atari” is in it: 


As you might have guessed, each letter in the names occupies one byte of 
memory, but then it is followed by a ‘\0\ or null character. Remember, part 
of the definition of a string is that there must be some termination signal, 
and that is what the null character represents. Although ‘\0’ appears to be 
two characters, it is really just one character value that requires two characters 
to show its meaning. Later we will be seeing other escape or control char¬ 
acters like the null character, but for now you only need to understand that 
AO' tells C that this is the end of the string. 

We can access individual characters within a string by indexing the vari¬ 
able like this: 


23 


Learning C on the ST 


a[4] 

which would have the value ‘e’ and T when the string was “Oliver” and 
“Atari” respectively. You may have thought the value of a[4J would be V 
and V, but the C language uses zero as the beginning of its array indices. 
So, instead of being able to look at any of the characters a[ 1J through a[10], 
the proper indices would be a[0] through a[9]. 


What to Declare? 


In general, C provides the ability to mix and match variables of different 
types via loose type checking. For this reason, it is possible to assign almost 
any type of variable to another variable of a completely different type. This 
functionality can be good in the sense that it allows for a great deal of flex¬ 
ibility in coding and may alleviate conversion problems for the programmer. 
However, it can also cause serious problems if not used properly. For this 
reason, it is recommended that as a beginning C programmer, you should not 
attempt to take advantage of this advanced feature. Wait until you have writ¬ 
ten several lengthy programs and feel completely comfortable with C before 
mixing and matching! 

Before moving on, we want to emphasize that when providing variable 
identifiers, we strongly recommend using identifiers that are descriptive or 
meaningful to the variable defined. As stated above, identifiers can be several 
characters long, although only the first eight are significant to the compiler. 
While there is nothing technically wrong with an identifier defined by a single 
character, readability should be a major consideration for proper program 
documentation. For example, a variable that represents someone's grade in 
a class is better suited by an identifier that is descriptive of its use rather than 
the letter x. For example, we can use 

float grade; 


Assigning Variables 


In our box example, we state that a variable inside the box, or memory lo¬ 
cation, can contain any value. To do this, we need a method for inputting 



C Revealed: Structure and Syntax 


values into a memory location. We can assign values to a variable by using 
the assignment statement. The assignment statement has the following general 
syntax: 

variable = expression 

where the variable on the left is assigned the value of the expression on the 
right. The symbol = is an assignment operator. If, for example, the variable 
represents a legal numeric variable identifier, an assignment statement might 
be: 


age = 29; 

where the value 29 is assigned to the memory location defined by the variable 
age. It is important to note that using the assignment operator here is not the 
same as using the equal sign used in basic algebra. In algebra, two values 
may be considered equal in value. C’s assignment operator simply means a 
variable is “assigned to” or “is given or becomes the value of” the expres¬ 
sion. As a final note on C variable assignment, the assignment may take place 
at the point of the declaration, like this: 

int a = 4; 

which results in space being allocated for a and that space being initialized 
to 4. This form of variable initialization comes in handy quite often. But keep 
in mind that because the value is assigned in the declaration part, it may be 
easy to overlook when you are reading through the code and could cause 
severe headaches when you are trying to track down a problem. 


Classes of Storage 


As the earlier pseudo-C code listing showed, C programs consist of a main 
routine and perhaps several other routines or functions. Up to this point, all 
the variables we have discussed are called automatic, or auto, variables. This 
is because the variable is automatically assigned a space in memory for that 
routine. In fact, we could have preceded all of our variable declarations with 
the reserved word auto like this: 


25 


Learning C on the ST 


auto int x; 

but the default storage class is automatic, so it is not necessary. In general, 
storage space for an auto variable is allocated whenever the function in which 
it resides is invoked, and when that function has completed execution, the 
storage space is relinquished for other program usage. For this reason, if a 
function contains an auto variable, you can never assume that that variable 
still contains the same value that was in it the last time the function was 
invoked. 

Alternatively, a variable declared to have the static storage class will 
always still have the most recent value assigned to it even if it was assigned 
several invocations ago. A static variable is declared like this: 

static int x; 

A third storage class of variables is the register type. By preceding the 
variable declaration with the reserved word register, like this: 

register int x; 

on some systems and with some compilers one of the microprocessor’s (the 
“brains” of the system) registers will be used to hold the variable’s value. 
This sort of declaration may help to speed your program up if you use it with 
a variable that is used quite intensely. Registers on the Atari ST are four 
bytes long, so keep in mind that only variables that have a data type of four 
bytes or less may be declared as register variables. 

The last storage class of variables is the extern class, which simply in¬ 
forms the compiler that this variable name is used externally to this routine, 
and both references to it are for the same space in memory. For example, 
with the following declarations 

int x; 

char y; 

main() 

{ 

extern int x; 
extern char y; 

you are informing the compiler that you know x and y were declared earlier 
outside of main() and that all references to both of them are for the memory 
space allocated by the external declarations. In this situation, the extern dec- 


26 



C Revealed: Structure and Syntax 


larations are not necessary because both sets of declarations are in the same 
file. But when you later progress to writing lengthy programs that are split 
up into multiple source code files, the extern declaration can come in quite 
handy. 


Expressing Ourselves 


We have introduced the concept of an expression during our discussion of 
the assignment statement. An expression is a sequence made up of variables, 
mathematical operators, and constants (via the #define directive). The fol¬ 
lowing are valid expressions: 

6 + 2 
a + b - 3 
pay - 3.25 
sum + 1 

As you recall, we use the assignment operator to assign the value of an 
expression to a memory location defined by an identifier. The operators used 
in an expression must be valid for the type of data they operate on. The most 
commonly used expressions are arithmetic. C is capable of performing most 
of the mathematical operations that you are familiar with: 

+ addition 

— subtraction 

* multiplication 

/ division 

% modulus or modulo division (remainder from integer division) 

In C, there also exists a unary minus (-) operator that may be used to 
change the sign of the operand. You should recognize the operation of +, 
-, *, and /; however, the operator % (modulo division) may be new to you. 
The modulo operator is used to calculate the remainder of integer division. 
For example: 

6 % 5 returns a value of 1 

8 % 4 returns a value of 0 


27 



Learning C on the ST 


9 % 6 returns a value of 3 

9 % 2 returns a value of 1 

The modulo operator has no meaning on real values and, as such, may not 
be used with them. 


Mathematical Order of Operation 


Mathematical operations are performed in a particular order. For example, 
multiplication and division are performed before addition or subtraction. To 
illustrate, what do you think the result of this operation will be: 

3 + 5*6 

If you read this example from left to right, you might interpret the result as 
48 instead of 33. The multiplication (5 * 6) is done before the addition (3 + 
5). This is a result of the order of operator precedence. Operators with the 
highest precedence are performed before operators with the lowest prece¬ 
dence. The unary (-) operator has the highest precedence of the mathematical 
operators. After it, the high precedence operators are *, /, and %. Operators 
with low precedence are + and -. 

If an operation consists of operators of the same precedence, then the 
calculation may be performed from left to right. In the example below, the 
left operation (addition) may be calculated first, followed by the second op¬ 
eration (subtraction). 

6 + 9-2 

As you probably determined, the result of this expression is 13. 

Parentheses are used to dictate the order of operations—the same way 
they are used in basic mathematics. The operations contained within paren¬ 
theses have a higher order of precedence than multiplication or division. For 
example, consider this assignment statement: 

i = (4 + 8)/2; 

The result is that the variable i is assigned a value of 6. The operation con¬ 
tained within the parentheses is performed before the division (/) operation. 


28 






C Revealed: Structure and Syntax 


Operations contained within a set of parentheses must follow the normal 
order of precedence. Can you determine the correct value of this statement: 

rate = {6 * 2 + 18) - (8 - 16/4); 

The correct value is 26. 

In summary, arithmetic expressions consist of a sequence of variables, 
operators, and/or constants. To eliminate any ambiguity in an arithmetic 
expression, a mathematical order of operations, or operator precedence, must 
be followed. 


Simple C Arithmetic 


We have covered several fundamental concepts in this chapter, and so it is 
appropriate that we should review them in a couple of simple program ex¬ 
amples. Consider the task of calculating your gas mileage—a simple problem 
that C on your Atari ST can easily calculate. How many miles do you travel 
to work or school? How many gallons of gas do you use? You know that 
mileage can be defined as the number of miles traveled divided by the number 
of gallons of gas used, right? If you answered these questions, you have 
defined the programming problem. If you determine that you traveled 200 
miles last week on 8 gallons of gas, you can write a program that looks like 
this: 

/♦This program calculates your gas mileage.*/ 

#include (stdio.h) 

float miles, gas, mileage; 

main() 

{ 

miles = 200; 
gas = 8; 

mileage = miles/gas; 
printf("%f'', mileage); 
getcharl); 

} 

When you run this program (after compiling and linking it), the result 


29 


Learning C on the ST 


is displayed in the upper left-hand comer of the display as 25.000000. Let’s 
quickly review this program step by step. We won’t explain every program 
in such excruciating detail, but it is important that you master these beginning 
concepts early. 

The first line is a comment that briefly describes the program’s purpose. 
The second line is our old friend, the #include directive, which tells the 
compiler to pull in the stdio.h header file. Next, our three float-type variables 
are declared. Notice that all three variables are declared on the same line with 
only one reference to the float type. This is quite acceptable in C and is highly 
recommended because it decreases the number of keystrokes necessary to 
enter programs in the editor. 

After our variables, we being the main() routine, which is surrounded 
by the beginning and ending bracket pair ({ and }). The program statements 
within the program block are the executable portion of the program. The first 
three lines, following the begin bracket ({), are all assignment statements. 
Each line is concluded with a semicolon to separate the individual program 
statements. The next statement is the printf statement. This statement will 
be covered in detail in the next chapter. All you need to know now is that 
it is responsible for displaying the result of our mileage calculation. In short, 
the information contained within the parentheses of the printf statement tells 
the compiler that we wish to display the value of the variable mileage in a 
“float” (%f) format. Finally, the last statement is a call to the getchar() 
routine, which we used earlier to allow the screen to stay unchanged until 
you press the RETURN key. 

The next example further illustrates the use of mathematical variables 
and assignment statements. Study the following program, and determine if 
you can understand each of the program's components. If you can’t, you 
should review the appropriate section. 

/*This program calculates a student's grade point average*/ 

#include (stdio.h) 

float testl, test2, test3, test4; 

float average; 

char name! ] = "Joe"; 

main() 

{ 

testl = 85; /*the following are assignments*/ 

test2 = 90; 

test3 = 78; 


30 





C Revealed: Structure and Syntax 


test4 = 79; 

average = (testl + test2 + test3 + test4)/4; 
printf("What is the student's name and test average?\n''); 
printf("%s\n'\ name); 
printf("%f", average); 
getchar(); 

} 

When you execute this program, your screen should read: 

What is the student's name and test average? 

Joe 

83.000000. 

Besides a couple of more printf variations, the only new item presented 
in this program is the ability to declare a string without explicitly specifying 
its length in the declaration. The compiler will determine the length for you 
based on the length of the string we have initialized it to (in this case, “name” 
occupies 4 bytes; don’t forget the trailing ‘NO’ termination character). Finally, 
let’s informally discuss how your programs should look in appearance, a con¬ 
cept called program formatting. 


Program Formatting 


Program formatting is a very important concept in C programming. In general 
terms, program formatting is the position on your screen or line that you 
begin a statement, insert a space, indent, etc. The reason for program for¬ 
matting is more than a compulsive desire for neatness. Its purpose is to make 
your programs more readable and thus reduce the amount of time needed for 
debugging or comprehension of a program by someone other than the pro¬ 
grammer who created it. 

Indentation will become a major factor in proper program formatting as 
we get into more complex source code. It is recommended that every time 
an indentation is necessary, you should indent a fixed number of spaces, say, 
3 or 4. This way, your programs will take on a much more professional 
appearance and will lend themselves to easy tracing by other programmers. 

We have covered a lot of ground, so we recommend that you take a 15- 
minute break, absorb what you have learned thus far, and then continue on 


31 



f 


Learning C on the ST _ 

to Chapter 3. If you don’t completely understand all the concepts presented 
in this chapter, go back and reread the appropriate sections. 

Glossary for Review 

syntax —the rules of a language that must be followed to communicate 
meanings properly 

bit —binary digit (one or zero) 

byte —group of 8 bits 

Quiz - 


1. What is wrong with this statement? 

#define top 10 

2. What are the two ways of including a separate source file in your code? 

3. Which data type occupies more memory—int or unsigned int? 

4. What does ‘\0’ mean in a string? 


32 




Chapter 3 


"\ 

C Statements 

v_ ) 


In this chapter, you will learn: 

• How to communicate with the user; the Input/Output statements. 

• What the printf and putchar routines are, and how to use them. 

• What the scant and getchar routines are, and how to use them. 

• How to write programs that make decisions. 






Learning C on the ST 


This chapter takes a deeper look into C. You have learned many of the 
fundamentals of program construction in the previous chapters. Most of the 
material you are about to read discusses various program statements that en¬ 
able you to write more complex programs. As you recall, program statements 
are the executable statements that appear within a program block, or { and } 
pair. We will explore fundamental control structures that give the computer 
the ability to make decisions. In addition, we will study interactive statements 
between the computer and the user. First, however, we need to examine how 
information is displayed, or output, to your screen. Let’s start by taking a 
closer look at these output routines. 


The printf Function 


You were briefly introduced to the printf function in the previous chapters. 
In this section, we cover the printf function and related output functions in 
more detail. These routines are very fundamental to programming in C. In 
fact, these are the functions that C uses to communicate the output processed 
by your programs so that you can readily understand its meaning. The general 
syntax for the printf function is: 

printf("cont_strng", argl, arg2, . . ., argn); 

where cont_strng is the control string that specifies how you want the output 
formatted, and argl, arg2, etc. are the actual arguments that are to be dis¬ 
played. We have already seen examples of how printf can be used to print 
out a literal string like this: 

printf("Welcome to C”); 

In this instance, the control string consists of a series of characters that we 
want to put directly on the screen. That is, there are no argument parameters 
used in this example. However, as our syntax notes, printf can be used to 
display both literal strings and other values as passed in the arguments. For 
example, if we want to display the value of the int variable goals, we would 
write it like this: 


printf(''%d", goals); 




C Statements 


In the control string, the % character signifies that the next character will be 
a conversion character specifying the format of the accompanying argument. 
In this case, the conversion character is a d, meaning decimal format. The 
nine conversion characters and their translations are: 

Conv. Char. Meaning 

c print a single character 

d print a decimal value 

e real (float) number; display in exponential 

notation (e.g., 4.56 E - 5) 
f real (float) number; display in decimal 

form (e.g., 45600000.00) 
g use the shorter of e or f above 

o display in unsigned octal form (base 8) 

s print a string 

x display in unsigned hexadecimal form 

(base 16) 

In addition to displaying just the arguments in the parameter list, we 
can mix literals in with the conversion characters to display statements with 
variables like this: 

printf("Mike is %d years old.", age); 

where the %d references the int variable age and would produce the following 
output (if age is set to 25): 

Mike is 25 years old. 

You could also have set up a string variable like this: 

char named = "Mike"; 
and then written the printf like this: 

printf("%s is %d years old,", name, age); 
and you would have achieved the same results. 


35 



Learning C on the ST 


All of your output has been on one line so far, but, of course, this need 
not always be the case. The control character \n may be used in printf state¬ 
ments to advance the output position the next line down on the screen. For 
example: 

printff'Mike is %d years oldVn.”, age); 
printf(”He is almost as old as %s.", name2); 

will display the following: 

Mike is 25 years old. 

He is almost as old as Mary. 

if, of course, age is equal to 25 and name2 is equal to Mary. These new line 
characters may be interspersed in your control string as you see fit to format 
your output properly. 

Speaking of output formatting, you may have noticed that printf will 
always display a float variable with 6 decimal positions even though you may 
only be interested in a couple of them. Consider the following declarations 
and C statements: 

float miles = 300; 
float gallons = 12; 
float mpg; 

mpg = miles/gallons; 

printf(”You got %5.2f miles per gallonVn”, mpg); 
printf("You got %5.2f miles per gallonVn”, miles/gallons); 

The output produced by the two printf statements will be: 

You got 25.00 miles per gallon 
You got 25.00 miles per gallon 


because the %5.2f control segment specifies that you desire a float value 
placed in a field that is five characters wide, and two of those characters, at 
most, are to be for the fractional portion of the number. 

You now know the basic concepts behind the printf function. Try a few 
short programs of your own where you print out combinations of floats, ints, 
and strings so that you thoroughly understand the principles. 


36 




C Statements 


The putchar Function 


In addition to the printf function, putchar is available to display one char¬ 
acter at a time. The syntax of putchar is: 

putchar(ch); 

where “ch” is a char value. So the following declarations: 

char a = 'a'; 
char b = 'b'; 
char c = 'c'; 

and putchar statements: 

putchar(a); 

putchar(b); 

putchar(c); 

will display the characters abc in the upper left-hand comer of your screen. 

Now that we have an understanding of the fundamental output functions 
available in C, let’s take a look at the functions available to receive input 
from the user. 


The scant Function 


The scanf function is quite similar to printf, and its syntax is: 

scanf("cont_strng", argl, arg2, . . argn); 

where the control string characters have the same meaning as those in printf 
except that there are no %u, %e, or %g, but there is a %h, which may be 
used to read in a short int. So, if we want to elicit input from the user, we 
could say: 


37 


Learning C on the ST 


printf{"What is your first name?\n"); 
scanf("%s", fname); 


Whatever you enter in at this prompt would then be put into the fname vari¬ 
able location. We could then print it back out like this: 

printff'Your name is %s.”, fname); 

Take a look at this interactive example: 


/♦This program asks for your name and age and displays them.*/ 

#include (stdio.h) 

char fnameflO]; 
int age; 

main!) 

{ 

printff'What is your first name?\n"); 
scanf("%s", fname); 
printfC'How old are you?\n"}; 
scanf("%d'',&age); 

printf("\n\nYour name is %s and you are %d years old . . fname, 
age); 

getchar(); 

} 


This program first asks you to enter your first name, and then it asks 
you for your age. The scanf call for inputting your age is a little bit different 
than the one used for your name, though. There is an ampersand (&) before 
the age parameter. This ampersand means that scanf is not using the actual 
age variable but rather a pointer to it. It is not really necessary for you to 
know the difference between the two here, since pointers will be discussed 
later. But you do need to know that for all arguments other than strings, you 
need to precede the variable name with an ampersand in the parameter list. 
Other than this difference, the scanf function is very easy to understand. So 
now let’s take a look at another routine that allows you as a programmer to 
accept input one character at a time. 


38 









C Statements 


The getchar Function 


You have already seen getchar used in some of the earlier programs in this 
book as a method of making the program stop so that you can look at the 
screen before the GEM desktop is redisplayed. The syntax of getchar is: 

ch = getchar{); 

Notice that ch, which is taken to be an int variable, is not passed as a pa¬ 
rameter to getchar, rather it is the value returned by the function. We will 
deal with functions and their methods of returning values in Chapter 5, but 
for now, when you see a variable on the left side of a statement and a function 
call on the right side, you may assume that the variable takes on the value 
returned by the function. So, we could accept characters and display them 
back out to the screen by having getchar and putchar right after each other 
like this: 

ch = getchar!); 
putchar(ch); 
ch = getchar!); 
putchar(ch); 
ch = getchar!); 

putchar(ch); /* and so on ... */ 

You have now been exposed to enough input/output routines to allow 
you to write even fairly sophisticated programs. But, in order for you to make 
your ST really work for you, you need to leam about how you can instruct 
it to make decisions. 


Statements of Choice: The Conditionals 


A fundamental feature of any computer is its ability to make decisions. The 
computer makes its decisions based on certain conditions or tests. If a par¬ 
ticular condition is met, the flow of control within a program is modified, 
and a different set of instructions is performed. Changing the flow of control 
of a program is essential to writing powerful programs. The programs de- 


39 




Learning C on the ST 


picted in this book thus far simply execute each statement sequentially. Such 
step-by-step execution limits the programmer’s ability to develop complex 
and useful programs. In the next section, we will learn how to change the 
flow of sequential instructions by looking at some fundamental control state¬ 
ments: the “if . . . else” compound statement and the switch statement. 


Decisions, Decisions . . . The Thinking ST 

For practical applications, the computer must have the ability to make de¬ 
cisions based on a conditional. What do we mean by a conditional? If, for 
example, you ask a question, and the response can only be true or false, such 
a response is an assertion to the circumstance of the question. The statement 
in C that creates the conditional is called the “if” statement. 

The “if” statement makes a decision by performing a decision test. The 
test has a true or false, yes or no, or precisely one or zero result based on 
the conditional expression. We say “one or zero” because the only way any 
computer can think is in terms of ones and zeros as bits. The syntax of the 
“if” statement is written as a compound statement, with the associated state¬ 
ment and the optional “else” statement. The structure of the simple “if” state¬ 
ment provides two related choices. The first option is: 

if (expression is nonzero) 

{ 

statement; 
statement2; 
statement3; 

} 

statement^ 

This first option states that if the expression is nonzero, statement 1 is 
executed, followed by statement2, etc., until the end of the block. Then 
statement4 would be executed. The conditional block is enclosed within the 
familiar beginning and ending brackets ({ and }). If the expression is zero, 
the conditional block is bypassed, and the next statement to be executed is 
statements 

A second structure of the “if” statement can be written as: 

if (expression is nonzero) 
statementl; 


40 






C Statements 


else 

statement2; 

statement3; 


The choice above adds a second option, the “else” portion, to our com¬ 
pound statement. The addition of “else” provides the possibility of one more 
statement being executed if the expression evaluates to zero. Thus, if the 
expression is nonzero (or true), statement 1 is executed, statement2 is skipped 
over, and statement3 is then executed. If the expression is zero (or false); 
statement 1 is skipped over, statement2 is executed, and then statement3 is 
executed. 

As we have said, when a conditional expression is executed, a rela¬ 
tionship is compared to determine if a condition is true or nonzero. If the 
condition is true, then the next statement or block of statements is executed. 
The condition itself is specified by relational clauses or operators. The re¬ 
lational operators used in C are: 


== (equal) 

!= (not equal) 

< (less than) 

> (greater than) 

<= (less than or equal to) 

>= (greater than or equal to) 


This simple example illustrates the conditional “if” statement: 

/•This program shows how to use the "if" statement.*/ 

#include (stdio.h) 
int numl, num2; 

mainO 

{ 

printf("Enter a number:"); 
scanf{"%d",&num1); 
printf("Enter another number:"); 
scanf("%d",&num2); 
if (numl == num2) 
printf("Both numbers are equal!\n"); 


41 



Learning C on the ST 


else 

printf("Both numbers are not equal!\n"); 
printff'lsn't this fun?!''); 
getchar(); 

} 

Let’s study this program. If you obey the computer and enter the num¬ 
bers 4 and 2, then the second printf statement is executed and displays: 

Both numbers are not equal! 

Isn't this fun?! 

Your curiosity gets the better of you, however. You decide to test the com¬ 
puter to see if it’s awake. You run the program a second time, but now you 
enter the same number twice. This time your ST responds with: 

Both numbers are equal! 

Isn't this fun?! 

Why? Our second test resulted in a condition that was true. Under a true 
condition, the first printf statement is executed. Our first test resulted in a 
false condition; therefore, the first printf statement is skipped over, while 
control of the program passes to the next available statement. 


A Couple of Common Errors with “if” Statements 

Beginning programmers in C often make a common mistake when using the 
“if” statement. The mistake is a logical one where the output received is an 
unexpected value. Specifically, we are referring to the result of the condi¬ 
tional when the condition is false and more than one statement follows the 
check. 

As you recall, when a condition is false, program flow skips the first 
statement and then continues with the first statement after the affirmative 
block. Execution, however, continues from that point, and the next statement 
is executed in successive order. This truth may not provide the results desired. 
To illustrate, let’s examine a portion from our previous example: 

if (numl == num2) 
printff'Both numbers are equal!\n"}; 


42 







C Statements 


else 

printf("Both numbers are not equal !\n''); 
printf("lsn't this fun?!"); 

When the value of numl is equal to num2, the condition is true and 
the program flows to the first printf statement and then to the third printf 
statement. However, suppose you don’t want the third printf statement to be 
executed when the condition is true. At present, this situation is not possible 
because the program flow will simply continue from the first to the third in 
a true condition. To do this, we can include all the alternatives in a single 
block of statements. The statements within the compound statement ({ and }) 
are executed in sequence. Consider this modification to our program: 

/*This is a modification to our program which shows how to use the "if" 
statement.*/ 

#include (stdio.h) 

int numl, num2; 

main() 

{ 

printf("Enter a number:"); 
scanf("%d",&num1); 
printf("Enter another number:"); 
scanf("%d",&num2); 
if (numl == num2) 
printf("Both numbers are equal!\n"); 
else 
{ 

printf("Both numbers are not equal!\n"); 
printf("lsn't this fun?!"); 

} 

getchar{); 

} 

In this situation, the message “Isn’t this fun?!” is displayed only when¬ 
ever two different numbers are entered. Because it is within the block of the 
“else” portion, as designated by the begin and end brackets ({ and }), it will 
never be executed when two identical numbers are entered. 

Another error with “if" statements that is usually encountered by pro¬ 
grammers familiar with other languages (especially PASCAL) is the use of 


43 



Learning C on the ST 


a semicolon at the end of the statement before the “else* clause. As all C 
programmers know, the semicolon is used at the end of all executable state¬ 
ments, but sometimes it is difficult to remember to use it even before an 
“else” if you are used to leaving it off! 

Also, always indent statements within a compound block (as shown above) 
so that it is easy to trace which statements are meant to go with which block. 
It may not appear to be very important now, but when you start writing more 
sophisticated code, you’ll be glad you did it. 

Nesting Your “if” Statements 

The statements that follow the true or false clauses may be of any kind. The 
statements can be printf statements, assignment statements, compound state¬ 
ments ({ and }), or even another “if” statement. When an “if” statement 
appears within another “if” statement, the statement is called a compound 
“if” statement. The technique of using multiple “if” statements within the 
same program structure is called nesting the “if” statement. There is no stead¬ 
fast restriction to the number of “if” statements that may appear nested within 
each other; this generally changes from compiler to compiler. However, such 
a complicated structure can become very difficult to follow logically. A sam¬ 
ple syntax of nested “if” statements might be: 

{ 

if (condition'll 
if (condition2) 
statement - !; 
else 

statement2; 

else 

statement3; 

} 

Notice the successive indentation of each “if” statement. Again, this for¬ 
matting makes programs much easier to understand. 

Why use a nested “if” statement? The answer is simple. Suppose you 
want a program to make a decision and then to perform one of two exclusive 
actions. In this case, an “if” statement will satisfy your needs. Suppose, how¬ 
ever, that you want the computer to provide three alternative actions instead 
of just two. To remedy this problem, we imbed or nest an additional decision 
test. To illustrate, let’s study the following example of calculating an em¬ 
ployee’s pay: 


44 








C Statements 


/•This program calculates pay including overtime*/ 

#include (stdio.h) 

#define RATE 4.00 
int hrs; 
float pay; 

main() 

{ 

printff'How many hours worked?”); 
scanf(”%d'\&hrs); 
if (hrs > 40) 
if (hrs > 45) 

{ 

pay = (RATE * 40.0) + (2.0 * (RATE * (hrs - 40))); 
printf("You earned double OT! Your pay is $%10.2f”, pay); 

} 

else 

{ 

pay = (RATE * 40.0) + (1.5 * (RATE * (hrs - 40))); 
printf(”You earned OT! Your pay is $%10.2f), pay); 

} 

else 

{ 

pay = RATE * hrs; 

printf(”Your pay this period is $%10.2f”, pay); 

} 

getchar(); 

} 

When the program is executed, the user is requested to input the number 
of hours worked. If a value of 40 hours or less is entered, a straight-time pay 
rate ($4.00 per hour) is provided. If a value greater than 40 hours but not 
exceeding 45 is entered, the employee earns overtime pay. A value of greater 
than 45 hours results in double overtime pay. For example, if you enter 40, 
the screen displays: 

Your pay this period is $ 160.00 


If you respond by entering 45 instead, the output is: 
You earned OT! Your pay is $ 190.00 


45 


Learning C on the ST 


Lastly, if you enter 50 instead, then the output is: 

You earned double OT! Your pay is $ 220.00 

In our sample program, the nested “if* statement provides three courses 
of action. Only one statement or statement block may follow the affirmative, 
or true clause, and appear before each “else.” 

Logical Operators 

We have not discussed logical operators. A conditional or logical expression 
is offered a greater amount of flexibility with the logical operators || (or), && 
(and) and ! (not, or negation). These operators give you the ability to write 
compound expressions. For example, suppose you input a value that you want 
tested to be between the value of 1 and 10. We can write: 

if (num > 1) && (num < 10) 

where the expression is only true if both assertions are true. Notice that both 
assertions are enclosed within a set of parentheses and separated by the “and” 
operator &&. The individual should be enclosed within parentheses to assure 
your intent is understood. 

The remaining two operators function in a similar fashion. The logical 
operator || (or) states that only one (or both) of the individual assertions must 
be true for the entire expression to be true. The logical operator! (not) pro- 


vides the exact opposite of a truth value and is called the logical negation 
(i.e., not true is false). The result of the three logical operators can be sum¬ 
marized in what is called a truth table as follows: 

A 

&& B Result 

A\\B Result 

!A 

Result 

T 

T T 

T T T 

T 

F 

T 

F F 

T F T 

F 

T 

F 

T F 

FT T 



F 

F F 

F F F 




The ?: (Conditional) Operator 

Through the simple “if” statement we have the ability to write a portion of 
code like this: 







C Statements 


if (hrs < 20) 
pay = hrs * 2.00; 
else 

pay = hrs * 4.00; 

where if the condition (hrs < 20) is true, then the pay will be based on a $2 
per hour rate; otherwise pay will be based on a $4 per hour rate. C provides 
an operator that may be used to abbreviate this sort of “if . . . else’* condition 
and it looks like this: 

pay = (hrs < 20) ? (hrs * 2.00): (hrs * 4.00); 

If the condition (hrs < 20) is true, the first expression to the right of the 
question mark (hrs * 2.00) will be evaluated with the result assigned to the 
variable on the left side of the equal sign (pay); otherwise, the expression 
after the colon (hrs * 4.00) will be evaluated with the result assigned to the 
variable. This operator may take some time to get used to, but it can cut out 
a few extra lines from your source file. 

The Switch Alternative 

As we have discussed, the opportunity exists for you to write a segment of 
code that checks a variable for a specific value and, based on that value, then 
executes certain statements. We have seen this in “if” statements like this: 

if (num == 5) 

printf("The number is five"); 
else 

printf("The number is not five"); 

With just two alternatives, this sort of statement is acceptable. However, what 
if we had to display a unique message if the number was any integer from 
one to five? The “if” statement would look like this: 

if (num — 1) 

printf("The number is one"); 
else if (num == 2) 
printf("The number is two"); 
else if (num == 3) 
printf("The number is three"); 


47 


Learning C on the ST 


else if (num -- 4} 

printf("The number is four"); 
else if (num == 5) 

printf("The number is five"); 
else 

printf("The number is not within one to five"); 

As you can see, it is allowable to have progressive checks in an “if” statement 
via the “else . . . if” option. This form of the “if” statement checks each 
condition and, once it finds a true one, it executes the statement block as¬ 
sociated with that condition and then skips over the rest of the “if” statement. 

This form of the “if” statement is indeed quite useful, but C provides 
another statement, the switch, that is much more readable than multiple else 
. . . if’s. The same lengthy “if” statement looks like this as a switch state¬ 
ment: 

switch (num) 

{ 

case 1: printf("The number is one"); 
break; 

case 2: printf("The number is two"); 
break; 

case 3: printf("The number is three"); 
break; 

case 4: printf("The number is four"); 
break; 

case 5: printf("The number is five"); 
break; 

default: printf("The number is not within one to five"); 

} 

As you might have guessed, the expression (num) is evaluated, and its value 
is compared against each of the values specified by the reserved word case. 
When a match is found, that case’s statement block is executed. However, 
at this point, the switch statement is not intelligent enough just to jump over 
the remaining checks. Rather, the break statement must be used to inform C 
that you do not wish to execute the very next statement. This is also why a 
begin/end bracket set ({}) is not necessary with any of the case statement 
blocks. Finally, if the expression does not equal any of the case values, the 
default block is executed. Note that there is no need for a break statement at 
the end of the default block because we are already at the end of the switch 
statement! 


48 











C Statements 


You have just completed another large portion of C programming basics. 
In Chapter 4 we cover more C statements that provide additional control over 
the flow of your programs—the looping statements. It’s time now to review 
what we have learned so far. Again, if you have trouble with a particular 
section, review it first before continuing on to Chapter 4. 


Quiz ~1 

1. What’s wrong with this statement? 

scanf("%d", num); 

2. How are getchar() and putchar() related? 

3. What is printed in this “if” statement? 

if (num) 

printf("The number is non-zero''); 
else 

printf("The number is zero"); 


49 










Chapter 4 

More Statements: The 
Looping Structures 

__ j 


In this chapter, you will learn: 

• How to use the “while” statement to construct a programming 
loop. 

• How to use the ++, —, +=, *=, /=, and %= operators. 

• What a counting and summing loop is, and how to use one. 

• How to use the “for” statement in a looping structure. 

• How to use the “do-while” statement to construct a loop. 

• What a nested loop is, and how to use one. 





Learning C on the ST 


In this chapter we are going to continue our discussion of C statements. 
In particular, we will examine a few additional control structures that allow 
you to write programs that repeat or loop. Looping programs, again, allow 
you to alter the flow of a program or its physical order of execution, se¬ 
quentially, from the first program statement to the last. C offers us several 
looping constructs to choose from: the “while” statement, “for" statement, 
and “do-while” statement. Let’s begin by looking at one of our favorites, the 
“while” statement. 


Programs That Repeat 


Thus far, we have discussed programs that are executed one line at a time. 
To execute a program; you double-click on its icon on the GEM desktop. If 
you wish to execute the program again, you must double-click on it a second 
time. One command in C allows you to execute a program, or a block within 
a program, repeatedly without double-clicking over and over again. To' do 
this, we can write a program that repeats itself over and over until a certain 
condition is true. Such a conditional control is called the “while" statement. 


The “while” Statement 


Similar to the “if” statement, a “while” statement tests a condition to deter¬ 
mine which of two courses of action to take. However, unlike the “if" state¬ 
ment, after the body of the “while” statement executes, the program flow 
“loops” back to the start of the “while” statement to test the condition again. 
As long as the tested condition is true, or nonzero, the first action is repeated 
endlessly until the condition tested is false, or zero. A sample syntax of the 
“while” loop can be written as: 

while (expression) 
statementl; 
statement2; 

where the reserved word “while” is followed by a logical expression, and 
statementl may represent one statement or a block of statements enclosed 
within the { and } brackets. The expression is, of course, a true or false 








More Statements: The Looping Structures 


condition. If the expression is a true test, the program will execute the state¬ 
ment (or statement block) once. After this execution, the program loops back 
to test the expression again. This looping construct continues executing the 
body of the loop until the expression results in a false, or zero, value. A false 
test tells the computer to skip statement 1 (or the statement block) and execute 
statement2. At this point, the looping “while" statement is completed, and 
the program will execute the next available statement. 

A special example of the “while" loop appears in the following pro¬ 
gram. We provide this special program to illustrate a point. In this example, 
you are requested to enter a number. Any response other than 13 will make 
the condition true and continue to display the statement following the “while" 
check endlessly. Such a loop is called an infinite loop. To stop an infinite 
loop on your ST, hold down the (Control) key and press the C key. Pressing 
the (Control) key while pressing another key is commonly written as CON- 
TROL/key, or, in our situation, CONTROL/C. This key combination has 
the effect of telling the computer to stop the program in progress. After you 
press CONTROL/C, the GEM desktop will be redisplayed, and you can run 
the program again or run something else. If the number entered is 13, the 
loop is avoided. 

/♦First "while" loop program*/ 

#include (stdio.h) 

#define MY_NUM 13 

int num; 

main() 

{ 

printf("Please enter a number:"); 
scanf{"%d", &num); 
while (num != 13) 

printf("You entered the number %d.\n", num); /* infinite loop */ 
printf("You entered the number 13!!!"); 
getcharO; 

} 

Try running the program above. A response of 13 causes the second 
printf to be executed, and the loop is avoided. To see the effect of the infinite 
loop, enter a number other than 13. The screen fills up so fast with the same 
line it is almost hard to see each new one being displayed. 


53 


Learning C on the ST 


The example above illustrates poor programming technique. Infinite loops 
should be avoided. A more practical use of the “while” loop is a structure 
where the loop is executed a specific number of times. This type of technique 
is discussed next. 


Counting Your Loops 


To control the number of times a loop executes, we can insert a counter that 
accumulates the number of iterations that occur. A counter is actually a vari¬ 
able that is assigned a value which increments each time the loop is executed. 
When the expression of a “while” statement reaches some predefined value, 
the condition results in a false value, and the flow of control moves out of 
the program loop. To illustrate, try this program: 

/*This program calculates products in a loop.*/ 

#include (stdio.h) 

int count, loopsjeft; 
int numl, num2; 

main!) 

{ 

count - 0; 
while (count < 5) 

{ 

printf("Enter a number:"); 
scanf("%d",&num1); 
printf("Enter another number:"); 
scanf("%d”,&num2); 

printf("The product of this set is %d\n", (numl * num2)); 
count = count + 1; 
loopsjeft = 5 - count; 

printf("This loop has executed %d time(s).\n", count); 

printf("This program will loop %d more time(s).\n\n", 
loopsjeft); 

} 

printf("\n\nTHIS PROGRAM HAS ENDED!!!"); 
getcharO; 

} 











More Statements: The Looping Structures _ 

This program illustrates two important points. First, notice the construc¬ 
tion of the “while” statement. The first course of action following the “while” 
line is a compound statement ({ and } pair). As we have said before, the 
compound statement may contain any number of statements and still be con¬ 
sidered a single block by the construct of the “while” loop. In other words, 
referring back to the syntax of the “while” loop, the compound statement 
represents statement 1. 

The second point of this example is the construction of the loop counter 
variable. We have appropriately called ours count. The counter is first ini¬ 
tialized to zero outside the loop. Within the loop, the counter is incremented 
by one each time the “while” loop executes. When the condition of the “while” 
statement is false, program flow exits from the loop and informs the user that 
the program has ended. The loop itself executes exactly five times. How do 
we know this? The number of times the loop executes is determined by the 
loop control variable, in this case the variable count. We compare the value 
assigned to the variable when it was initialized (count = 0;) to the value of 
the condition test when the test is false (count < 5). We can set the count 
equal to 1 and then set the condition test as (count <= 5). Either way, the 
loop executes five times. Be careful when setting the number of controlled 
loops. If the counter above was initially set to zero and the decision test was 
set as (count <= 5), the loop would execute six times and not five! 


Loops That Sum 


We can also write a program that accumulates a result within a loop. Such 
a loop is used to sum a list of values and is called an accumulator, or sum¬ 
ming, loop. This type of loop looks just like the counter just discussed; in 
fact, the difference between the two is almost negligible. The point to be 
made is that a summing loop doesn't need to be dependent upon the value 
of the counting variable within a “while” loop. The two loops, a counter and 
an accumulator, may coexist within the same "while” statement. For ex¬ 
ample: 

/•This program sums the five numbers you enter.*/ 

#include (stdio.h) 

int num, count, sum; 


55 


Learning C on the ST 


main() 

{ 

printfC'This program will sum a list of numberAn"); 

printf(”You will be prompted to enter 5 numbers and you\n"); 

printf(”should press (RETURN) after each entry An”}; 

count = 0; 

sum = 0; 

while (count < 5} 

{ 

printf(”Enter a number:"); 
scanf(”%d”,&num); 
sum = sum + num; 

printf{"The sum of your numbers so far is %d\n", sum); 
count = count + 1; 

printf(”We have made %d pases through the loop.\n\n”, count); 

> 

printf("The program is over and the sum of your numbers is %d.", 

sum); 

getcharO; 

} 


As you can see, the variables count and sum are used to see how many 
times we have been through the loop and the total of the numbers entered so 
far, respectively. A sample run of the program might look like this: 

This program will sum a list of numbers: 

You will be prompted to enter 5 numbers and you should press 
(RETURN) after each entry. 

Enter a number: 1 

The sum of your numbers so far is 1 
We have made 1 passes through the loop. 

Enter a number: 2 

The sum of your numbers so far is 3 
We have made 2 passes through the loop. 

Enter a number: 3 

The sum of your numbers so far is 6 
We have made 3 passes through the loop. 

Enter a number: 4 

The sum of your numbers so far is 10 
We have made 4 passes through the loop. 


56 








More Statements: The Looping Structures 


Enter a number: 5 

The sum of your numbers so far is 15 

We have made 5 passes through the loop. 

The program is over and the sum of your numbers is 15. 

Every time you enter a number, the sum so far is reported, as is the 
number of times we have gone through the loop. After five passes, a message 
is displayed showing you that the loop is finished, and your final total is 
shown. Before we look at another way of looping in your programs, let’s 
look at a few abbreviations C provides for some simple mathematical oper¬ 
ations that come in quite handy, especially when you are working with loops. 


More Mathematical Operators 


In the example above, the statement we used to increment our count variable 
was: 


count = count + 1; 

.which is rather verbose. Fortunately C provides this shorthand notation for 
incrementing variables by one: 

var+ + ; OR ++var; 

where var+ + increments the variable after its value has been used (post¬ 
incrementing), and + +var increments it before its value has been used (pre¬ 
incrementing). The difference between the two makes no difference to us in 
our program, since we can simply replace the line: 

count = count + 1; 


with: 


count+ + ; 


or: 


57 



Learning C on the ST 


++count; 

Either way, the variable count will be incremented every time either of these 
statements is executed. To see exactly how the two formats differ, try this 
program: 

/*This program shows the difference between ++var/var+ + */ 

#include (stdio.h) 

int numl; 
main() 

{ 

numl = 1; 

printf("With + + num1, we get %d,\n”, + + num1); 
printf("with num1 + + , we get %d,\n", num1 + + ); 
printf("and the final value of numl is %d.", numl); 
getchar(); 

} 

Although you should not be surprised to discover that the value printed 
in the first printf is 2, you may be puzzled to see that the second printf also 
displays a value of 2. As we said, numl + + does not increment the variable 
numl until after it’s current value has been used in that statement. This is 
why the third printf shows the value of numl to be 3, which is what we 
expect it to be after incrementing it twice. 

C also provides the same type of incremental operators for subtraction, 
namely: 

var-~; AND --var; 

where the same rules of post-decrementing and pre-decrementing apply. In 
addition to these mathematical shortcuts, C also provides the following ab¬ 
breviations, which are shown with their general equivalents: 


General Form 


C Abbreviation 

numl 

= numl 

+ 

num2; 

numl 

+= num2; 

numl 

= numl 

- 

num2; 

numl 

-= num2; 

numl 

= numl 

* 

num2; 

numl 

*= num2; 

numl 

= numl 

/ 

num2; 

num 1 

/= num2; 

numl 

= numl 

% num2; 

numl %= num2; 


58 





More Statements: The Looping Structures 


So, if for some reason we would have wished to increment our variable by, 
say 3, every time through a “while” loop, we could abbreviate this: 

count = count + 3; 
with this: 

count += 3; 

These shorter forms are quite handy in saving keystrokes when entering 
a long program, and they should be used when possible. Now let's take a 
look at another method of looping, the “for” statement. 


The “for” Statement 


The “for" statement, like the “while” statement, is a looping structure used 
to execute statements repeatedly within a program. The “for" statement spec¬ 
ifies the number of times a statement or compound statement executes with 
a built-in counter. In addition, “for” allows you to initialize and perform 
regular updates on other variables. Its general syntax is: 

for (init; test; change) 
statement!; 
statement2; 

where init is where your variable initializations are specified, test is where 
the determination to perform another iteration is located (similar to the test 
in a “while" loop), and change is where you describe your updates to vari¬ 
ables that will be performed on each iteration. In its simplest form, a “for” 
loop might look like this: 

for (numl = 0; numl < 5; num1-r + ) 
printf{"The current value of numl is %d.\n", numl); 

where numl is first set to zero, the check is made to determine if (numl < 
5) is true, and if so, the printf statement is executed. What do you think is 
displayed by this segment? If you guessed the numbers zero through four. 


59 



Learning C on the ST 


you are correct. What do you think would be displayed if we changed the 
third portion of the loop like this: 

for (numl = 0; numl < 5; ++num1) 
printff'The current value of numl is %d.\n", numl); 

If you think the numbers one through five will be displayed, you are wrong! 
Although we know the pre-increment operator (as in + 4-numl) increments 
the variable before its value is used, in the “for” loop, the third (or update) 
portion of the statement does not get executed until after the first iteration. 
So, the results for this version of the “for” loop are identical to those of the 
previous one—zero through four. 

What is the value of a loop? We have briefly discussed how a loop 
provides the programmer with additional control over the flow of a program. 
But a loop can be a real time-saver too. To illustrate, consider this program 
segment: 

for (x = 1; x <= 100; x + + ) 

{ 

scanf("%d",&num); 

printf("%d\n",num); 

} 

When the program executes this loop, the values assigned to a- are printed 
from the selected range of 1 to 100. In other words, the value of a is printed 
100 times. An alternative method is simply to include 100 scanf and printf 
statements. Which method is more practical? Let’s study a simple example: 

/*This program performs simple accounting.*/ 

#include (stdio.h) 

float sum, exp_sum, mth_net_inc, mth_exp; 
float inc_ave, exp_ave; 
int income, expenses; 

main() 

{ 

sum = 0; 
exp_sum = 0; 

printf("Enter your monthly income . . An"); 
for (income = 1; income <= 12; income + + ) 

{ 

printff'Enter income for month %d: ", income); 


60 














More Statements: The Looping Structures 


scanf("%f",&mth_net_inc); 
sum += mth_net_inc; 

} 

printf{"\n\nEnter your monthly expenses . . An"); 
for (expenses = 1; expenses <= 12; expenses+ + ) 

{ 

printfC'Enter expenses for month %d: ", expenses); 
scanf("%f", &mth_exp); 
exp_sum +- mth_exp; 

} 

printf("\n\nYour total net income for the year is $%10.2f\n", sum); 
printf("Your total expenses for the year is $%10.2f\n", exp_sum); 
inc_ave = sum / 12; 
exp_ave = exp_sum / 12; 

printf("Your ave. monthly net income is $%10.2f\n", inc_ave); 
printf("Your ave. monthly expenses is $%10.2f\n", exp_ave); 
if (inc_ave < exp_ave) 
printf("Your accounts are in the red!\n"); 
else 

printf("Your accounts are in the black!\n"); 
getchar(); 

} 

When you execute this program, you are requested to enter your net 
income for each of the 12 months first and then each monthly expense for 
the same period. Examine the “for” statement itself. Both loops contain an 
ending value of 12, which specifies that each loop will execute exactly 12 
times (1 to 12). The statement following the “for” statement is a compound 
set ({ and }). All statements contained within this block are executed 12 times. 
This is how we are able to request input via scanf 12 times. In addition, we 
included an accumulator within each loop to sum the amounts of net income 
and our monthly expenses. When each loop is finished, program flow exits 
the loop and executes the next available statement. Note that any range that 
counts 12 times can be used in this example. In other words, a range from 
12 to 23 will loop 12 times as well. The beginning value and the ending 
value parameters of a loop that increments by one between these two values 
are what is important. 

The inclusion of a compound statement following each “for” statement 
is important. Why? Because we want the I/O (printf and scanf) and the 
accumulator to execute 12 times. If the { and } pair is omitted, only the first 
statement following the “for” statement is executed as part of the loop. When 


61 


Learning C on the ST 


the loop is finished, program flow would pass to the scanf, which would 
only be executed once, and everything would be a mess! 

You should be familiar with the components of the rest of this program. 
Our goal, however, was to simplify a programming task via the “for” state¬ 
ment, and although we have a fairly compact program (since we don't have 
12 separate printfs and scanfs for the I/O), we could compact things even 
more like this: 

/*This program performs simple accounting, but in less code*/ 

#include (stdio.h) 

float sum, exp_sum, mth_net_inc, mth_exp; 
int counter; 

mainO 

{ 

sum = 0; 
exp_sum = 0; 

printf("Enter your monthly income and expenses . . An"); 
for (counter = 1; counter <= 12; counters-+ ) 

{ 

printf("Enter income for month %d: ", counter); 

scanf("%f",&mth_net_inc); 

sum += mth_net_inc; 

printf("Enter expenses for month %d: ", counter); 

scanf("%f",&mth_exp); 

exp_sum += mth_exp;} 

} 

printf("\n\nYour total net income for the year is $%10.2f\n”, sum); 
printf("Your total expenses for the year is $%10.2f\n", exp_sum); 
printf("Your ave. monthly net income is $%10.2f\n", sum / 12); 
printf("Your ave. monthly expenses is $%10.2f\n", exp_sum / 12); 
if ((sum / 12) < (exp_sum / 12)) 
printf("Your accounts are in the red!\n"); 
else 

printf("Your accounts are in the black!\n"); 
getchar(); 

} 

In this version, we have removed a couple of somewhat unnecessary 
variables: inc_ave and exp_ave. More importantly, however, is the fact that 
we were able to consolidate our income and expense loops into one. Here 










More Statements: The Looping Structures 


we enter each month’s income followed immediately by each month's ex¬ 
penses. Because of this, we were able to replace the loop counting variables 
income and expenses with the generic one, counter. In our haste to cut out 
variables and code, we actually added a bit of processing time to the program, 
since it now has to calculate both the average income (sum / 12) and the 
average expenses (exp_sum / 12) in both the printf statements and the “if" 
statement. 

The “for” statement is a very flexible control structure. A program may 
contain several “for” statements within the same program block. In addition, 
we can write a program that specifies the number of iterations a “for” state¬ 
ment loops before it executes by predefining the ending value of the incre¬ 
ment (e.g., 1 to 5). We can also write a program that lets the user interac¬ 
tively specify the number of iterations as the program executes (before the 
actual loop executes). The following example is another slightly modified 
version of our accountant program: 


/*This program performs simple accounting, but in less code*/ 
#include (stdio.h) 

float sum, exp-sum, mth_net_inc, mth_exp; 
int num_mnths, counter; 
main() 

{ 

sum = 0; 
exp_sum - 0; 

printf("How many months do you wish to do: "); 
scant ("%d",&num_mnths); 

printf("Enter your monthly income and expenses. . An''); 
for (counter = 1; counter <= num_mnths; counter-!--)-) 

{ 

printf("Enter income for month %d: ", counter); 

scanf("%f",&mth_net_inc); 

sum += mth_net_inc; 

printff'Enter expenses for month %d: ", counter); 

scanf("%f”,&mth_exp); 

exp_sum += mth_exp;} 

} 

printf{"\n\nYour total net income for the year is $%10.2f\n", sum); 
printf("Your total expenses for the year is $%10.2f\n", exp_sum); 
printf("Your ave. monthly net income is $%10.2f\n", sum / 
num_mnths); 


63 


Learning C on the ST 


printf("Your ave. monthly expenses is $%10.2f\n", exp_sum / 
num.mnths); 

if ((sum / num_mnths) < (exp_sum / num_mnths)) 
printf("Your accounts are in the red!\n”); 
else 

printf("Your accounts are in the black!\n"); 
getchar{); 

} 

The primary difference between this program and the last version of the 
accountant program is that the user interactively selects the number of times 
the loop executes. This task is accomplished with the scanf statement, which 
assigns a value to the variable representing the ending value of the range in 
the “for” statement. Specifically, we used the variable num.mnths to rep¬ 
resent the ending value. Therefore, from our previous program, the statement: 

for (counter = 1; counter <= 12; counters+) 
becomes: 

for (counter = 1; counter <= num_mnths; counters-+ ) 

where the value assigned to the variable num.mnths is the ending value of 
the “for” loop that replaces the value 12. This value must be assigned before 
the “for” statement executes. In our example, the user is prompted to enter 
the desired value just before the “for” statement. Such a method provides a 
larger degree of flexibility. You can select 3 months, 6 months, 12 months, 
or any whole number that you desire. 

Referring to the rest of the program, notice that the formats of the for¬ 
mulas for the printf statements that display the average value have been changed 
to incorporate the num.mnths variable. If we simply left these denominators 
at 12, the resulting values would only be correct when the user entered in 12 
for the number of months to process. 

Although we have not explicitly said so up to now, the “for” loop may 
be used to loop backwards like this: 


for (i = 100; i > 0; i—) 
printf(''We are looping backwards!!!\n"); 


64 


This sort of “for” loop is very convenient to use in certain situations, but 
basically operates just like the upwards counting loops we have already seen. 












More Statements: The Looping Structures 


That is, after initialization (i = 100), if the condition is true (i > 100), the 
associated block is executed, and each time the update portion (i—) is per¬ 
formed. In the next section, we discuss one more looping control structure, 
the “do-while” statement. This structure is the last of C’s three looping op¬ 
tions. 


The “do-while” Statement 


The “do-while” statement consists of two parts: the body and the termination 
condition. It is essentially an upside-down “while” statement in which the 
condition is not checked until the loop has performed at least one iteration. 
As with the “while” statement, the programmer should include a condition 
where the expression will be false and the flow exits the loop. Otherwise, 
the loop won’t terminate, and we face the problem of an infinite loop. This 
simple program example satisifes this requirement: 

/*This program calculates your family budget.*/ 

include (stdio.h) 

float rent, utilities, groceries, car, misc; 

float netJncome, budget; 

main{) 

{ 

printf("This program calculates your monthly family budgetAn ); 

printfC'The program will quit when you are over budgetAn''); 

do 

{ 

printf("Rent: "); 
scanf("%f",&rent); 
printfC'Utilities: "); 
scanf("%f",&utilities); 
printf("Groceries: "); 
scanf("%f",&groceries); 
printf("Car payment: "); 
scanf("%f",&car); 
printf("Miscellaneous: "); 
scanf("%f",&misc); 
printf("Total net income: "); 
scanf("%f',&net_income); 


65 


Learning C on the ST 


budget = net_income - (rent + utilities + groceries + car + 
misc); 

printf("\n\nYou are left over with $%10.2f\n", budget); 

} while (budget > 0); 

printf("\n\nYour expenses exceeded your net income!!!"); 
getcharO; 

} 

This program calculates a family’s monthly budget. All the statements 
within the body of the loop will execute at least once. The program first 
instructs the user to enter a series of expenses (i.e., rent, utilities, groceries) 
followed by the user’s net income. Expenses are totaled and then subtracted 
from net income to arrive at a budget for this particular month. If net income 
is greater than the expenses, the program repeats another calculation (cal¬ 
culate another month). The mechanism used to exit the loop is provided by 
the condition at the end of the loop (while (budget > 0)). Specifically, when 
the net income for a particular month is less than the total amount of ex¬ 
penses, program flow exits the loop and the program ends. 

“while” vs. “do-while” 

Because of the similarity in structure of the “while” loop and the “do-while” 
loop, these two loops are often compared. These two loops, however, have 
one significant difference regarding their execution. Both loops must test a 
condition in order to execute the loop itself. Performing the decision test 
differentiates between the two. Specifically, the “while” statement tests the 
loop condition before executing the body of the loop. On the other hand, the 
“do-while” statement tests the loop condition at the end of the loop. In other 
words, the “do-while” statement always executes its loop body at least once. 
The choice of which to use is entirely up to you, but for most instances, the 
“while” version will be the most appropriate. 


Nested Loops 


What is a nested loop? Nested loops are loops within a loop. We have il¬ 
lustrated nesting program structures before, such as the compound { and } 
pair and nested “if” statements. The logic for nesting a loop, such as the 


66 








More Statements: The Looping Structures 


“while,” “do-while,” or “for” statement is similar in execution. In simple 
terms, the way a nested loop works is that the outer loop executes its initial 
task, and then waits until the inner loop completes all of its loops or tasks. 
Here's a simple example that illustrates a nested “for” statement: 

/•This program shows how nested loops work. */ 

#include (stdio.h) 

int loopl, loop2, cntl, cnt2; 

main(} 

{ 

printf("How many times do you want the outer loop to execute: "); 
scanf("%d'\&loop1}; 

printf<"How many times do you want the inner loop to execute: "); 

scanf("%d",&loop2); 

for (cntl = 1; cntl <= loopl; cnt1 + + ) 

{ 

printf("Outer loop iteration #%d\n" f cntl); 
for (cnt2 = 1; cnt2 <= loop2; cnt2 + + ) 
printf("lnner loop iteration #%d\n", cnt2); 

} 

printf("\n\nAII loops are now complete.”); 
getcharO; 

} 

This program requests users to select the number of times they want the 
nested loop to execute. First, you enter a value for the outer loop, and then 
you are requested to enter a value for the inner loop. In each case, the outer 
loop and inner loop will execute the number of times assigned to the ending 
value (loopl and loop2) of the control variable (cntl and cnt2). 


One More Loop: The goto Statement 


As we discussed, the “while” and “do-while” statements are called condi¬ 
tional looping statements. In addition, we learned how to use the “if” and 
“switch” statements to perform a conditional branch. Conditional structures 
execute a selected number of statements depending on the value of an expres¬ 
sion (the condition). C offers one additional looping structure that alters pro- 


67 


Learning C on the ST 


gram flow unconditionally. This type of unconditional branching is called the 

goto statement. 

The goto statement is common among other programming languages, 
such as BASIC. However, it is not generally recommended because of its 
uncontrollable nature and its inconvenience when trying to trace program flow. 
Its purpose is to pass control of the program flow unconditionally from one 
point in a program to another, skipping the execution of any statement(s) 
between the two points. A common use of the goto statement is to exit from 
a loop. 

The point where the flow of program control is transferred to is iden¬ 
tified with a “label.” The general format of the goto statement is: 

goto label; 

where “label” represents a valid name with naming rules identical to those 
of variables. For example: 

here: printff'We used a goto statements"); 

“here” is a valid label and the statement: 

goto here; 

although it does not sound grammatically correct, causes program flow to 
jump to the printf statement denoted by the “here” label. 

C offers several control structures designed for solid structured pro¬ 
gramming technique. The goto statement is not one of them. Programs can 
be written efficiently without the goto statement. We were tempted to omit 
this statement from our discussion; however, you do have the right to know 
it is available. Compare it to the control structures that have only one exit 
and one entry. Which structure would you like to read, follow, or debug? 
We think you will agree that you should avoid using the goto statement. 

This chapter taught you several concepts of program loops. You learned 
about the three important looping structures—“while” loops, “for” state¬ 
ments, and “do-while” loops. You learned about counting loops and loops 
that accumulate. In addition, you learned about the differences between loops. 

It’s time to take another break. Review the summaries and exercises that 
follow. When you’re ready, continue on to Chapter 5 to study a very im¬ 
portant feature of structured programming in C—functions. 


68 









More Statements: The Looping Structures 


Glossary for Review 

infinite loop —a loop that will never end because the condition to be 
tested never evaluates to a terminating value 

pre-incrementing —adding one to the value of a variable before its value 
is used 

post-incrementing —adding one to the value of a variable after its value 
is used 

pre-decrementing —subtracting one from the value of a variable before 
its value is used 

post-decrementing —subtracting one from the value of a variable after 
its value is used 


Quiz 


1. What is the primary difference between a “while” loop and a “do-while” 
loop? 

2. What is the difference between num++ and + + num? 

3. How many iterations will the following loop perform? 

for (num = 5; num < 5; num + + ) 
printf("Hi thereAn"}; 


69 











Chapter 5 


C Functions 

_ ) 


In this chapter, you will learn: 

• How to write a program that features structured design. 

• What a function is, and how to define one. 

• How to use a function correctly with variables and parameters. 

• How to use the return statement. 



Learning C on the ST 


Thus far, we have covered many important programming tools from 
which you can build useful programs. All of our examples have used a single 
program module or block to accomplish a single task. In this chapter, we are 
going to discuss ways of putting together a group of modules, each perform¬ 
ing a single task. When the individual modules are taken collectively, a larger 
programming task is performed. This type of programming is called modular 
programming. One mechanism used to accomplish this goal is called a func¬ 
tion. 


Structured Design 


Large programming problems are often broken down into smaller subpro¬ 
grams. Subprograms are further divided into smaller subprograms until each 
subprogram consists of a few manageable statements. These “miniprograms” 
are generally easier to solve than the original “macro” program. Each sub¬ 
program performs a particular task, such as processing data mathematically 
or printing results. The individual subprograms are treated as modules. The 
final program consists of a collection of these individual modules. This type 
of programming design is referred to as structured because of the hierarchical 
tree nature of modular programming. 

In this chapter, we are going to discuss a particular type of structured 
design, called top-down design. Top-down design (sometimes called stepwise 
refinement) is a methodical approach where the original macro problem is 
represented by a main module. The main module could consist of the major 
programming steps required to solve a problem. The main module then calls 
upon the individual subprograms to solve specific tasks. When all the simpler 
tasks are solved, we have a solution to our original problem. 

As stated, the main module is divided into smaller and smaller modules. 
Each module represents a specific level of programming that performs a task 
independently of the other modules. For example, the main module can be 
depicted as level 0, the next set of modules as level I, and so on. Any module 
within this tree structure at a higher level can make demands on any module 
at a lower level. 

Writing programs as a collection of modules is a very important feature 
of the C language. Let’s now turn our attention to the logical method used 
to identify a subprogram or module—the function. 









C Functions 


What Is a Function? 


A function is used to identify a collection of C statements that perform a 
specific task. The function represents a subprogram and is executed each time 
the function is invoked. To access a function, you assign it a name and then 
“call” it by name from some other point in the program. Once a function has 
executed, the flow of program control returns to the calling routine. For ex¬ 
ample, we have already worked with the functions printf and scanf (to name 
but two). Although they may appear to you to be some magical black box 
in which you send some information and get other information back (in the 
form of a display, assignment, etc.), they are simply C functions that, when 
invoked, cause program flow to start executing their statements. Program 
flow is then resumed with the statement that immediately follows the one that 
called the function. 


What Does a Function Consist Of? 


The structure of the function is just like the program examples that we have 
been discussing. A function consists of two parts: the heading and the body. 
The heading, or name assigned to a function, is simply the label you use to 
refer to the function. The body is the sequence of program statements (in¬ 
cluding declarations) specific to a function. Generally, there are no restric¬ 
tions placed on the number of statements that a function may contain. How¬ 
ever, one of the primary purposes of using functions is to break down the 
programming problem into more meaningful tasks for both readability and to 
reduce the number of identical code blocks. For these reasons, you really 
don’t want to have a function that is ridiculously long and could be broken 
down further. The function may look just like a miniprogram, complete with 
a section for declarations and a { and } pair to show the beginning and ending 
points of the function. 


Where Is a Function Placed? 


In general, a function may be placed anywhere in your source code in re¬ 
lationship to the main routine. Unlike languages such as PASCAL, the func- 


73 




Learning C on the ST ___ 

tions do not have to be declared before the main routine, so it is up to you 
to decide where you would like to place them. We have seen code that places 
main at the beginning of the source and at the end. While neither may be 
more correct, we prefer to place main at the beginning. 


How Are Functions Used? 


Suppose you have a program that you wish to perform a number of statements 
over and over again. We can simply rewrite the same sequential set of state¬ 
ments each time they are required; or, we can “call” a miniprogram or func¬ 
tion to accomplish the same task. When the task is completed, we then con¬ 
tinue on about our business. For example, let’s examine the following program. 
It contains two mathematical functions, one for addition and one for multi¬ 
plication; 

/"This program shows how functions may be used.*/ 

#include (stdio.h) 

float numl, num2, sum, product; 

main() 

{ 

printf("Enter a number: "); 
scanf{"%f”,&num1); 
printf("Enter another number: ”); 
scanf("%f",&num2); 

printf<"\n\nWe are now computing the addition . . . \n"); 
add{); 

printf{"\n\nWe are now computing the multiplication . . . \n">; 
mult(); 

printf("\n\nThe program is finished!''); 
getchar(); 

} 

add() 

{ 

sum = numl + num2; 

printf{"The sum of your numbers is %f.\n", sum); 

} 


74 








C Functions 


mult() 

{ 

product - numl * num2; 

printf("The product of your numbers is %f.\n", product); 

} 

We think it’s important that you understand the flow of program control 
in our simple example above. Let’s take a look step by step. 

When you execute the program above, you are asked to input two num¬ 
bers. This is accomplished by the two printf and scanf combinations in the 
main routine. Next, a message is displayed that informs you that the addition 
is about to be performed. At this point, the routine add() is invoked, and 
control jumps to this function. The add() routine has all of its statements 
enclosed with a { and } pair. The first statement executed is the calculation 
of the sum. Next, the sum is printed out, and the function ends. Then, control 
returns to the main routine, and a message is displayed indicating that the 
multiplication is about to be performed. As you can see, when we left the 
main routine, we were about to perform this printf statement, and as soon 
as we return, it is executed. At this point, the mult() function is invoked, 
and control is passed to it, just as it was to add( ). First the product is cal¬ 
culated, and then it is displayed. This function then ends, and control is trans¬ 
ferred back to the main routine, where the message “The program is fin¬ 
ished!” is displayed. 

Two important points to note are that the names of the functions add() 
and mult( ) appear at the beginning of those functions just as tnain( ) appears 
at the beginning of the main routine. In addition, note again that the func¬ 
tion's statements are enclosed with a { and } end pair, just like those of the 
main routine, and that compound statements would appear within braces just 
like they have in main(). 

Writing programs, such as the one just discussed, is easily accomplished 
using top-down design. The first step in writing the actual program code 
involves breaking down the main program into smaller manageable minipro¬ 
grams. The logic of the main program is first developed. In our previous 
example, for instance, we put all the little initializing input/output statements 
in the body of the main program. Next, we break the building blocks of the 
program into separate smaller tasks, such as a function to do addition and its 
output, another to do multiplication and its output, etc. Whenever such a task 
is required, we simply call on that particular procedure again. Remember, 
we can call a function from any point in the main program as many times as 
we want. We can even have one function call another function. This should 


75 



Learning C on the ST 


be easy to visualize, since main() is nothing more than a special function 
itself. 

Obviously, a function may contain a miniprogram that completes a more 
complex task than adding or multiplying two numbers. However, the concept 
is the same. It is important that you understand the logic behind structured 
programming with a top-down design where large programs are really a col¬ 
lection of smaller ones. Later in this book, we will introduce applications 
that utilize this very important feature. 


Using Variables with Functions 


Understanding and defining how a procedure works is a simple concept. 
However, there are a few additional rules that we need to discuss. In our 
previous example, we created a program that transmitted information from a 
procedure back to the main program. All of our variables were global to the 
program, and so any function was permitted to modify them at any time 
(recall the global variable description in Chapter 2). The way we modified 
these variables via the functions add() and mult( ) was via their global na- 
ture. However, variabies can be passed from one Function to another, and 
therefore they need not be global to the program in order to be modi¬ 
fied.There are actually two different methods of passing values to a func¬ 
tion—by value and by address. These two methods differ greatly and will 
be discussed later. First, let’s review a bit and further discuss the concept of 
global versus local variables. 


Global vs. Local Variables 

Before you write a function that uses variables, you must first determine how 
it’s going to be used and what effect the variable will have on the rest of the 
program. In C, there are rules for governing the variables in a function, which 
are called scope rules. The scope of a variable is defining its accessibility to 
the rest of the program. For example, if a variable named a is used in a 
function, do you want its value accessible by other functions? Our mathe¬ 
matics program used this type of variable, a global variable. A global variable 
has meaning throughout the program. 

On the other hand, suppose you want a function that uses a variable that 
has no effect outside the function itself. This type of variable is called a local 


76 














C Functions 


variable. A local variable is one that only has meaning while the body of the 
function where it resides is executing. When execution of this function is 
complete, a local variable no longer exists (unless it is declared to be a static 
variable). 

How do we identify or define the scope of a variable? As you recall, 
all variables must first be declared before they are used in a program. This 
rule applies whether the variable is used in the body of the main function or 
within another function. It’s where you place the variable declaration that 
defines its scope. If the variable is declared within a function’s { and } block, 
it may be accessed only by that function. If however, the variable is declared 
outside the main’s { and } block (as our variables were), then it is accessible 
by the entire program. 

Beware if you declare a variable with the same name both globally and 
locally to a function. Why? Because once the function transfers control back 
to the calling routine, the local variable will no longer exist as far as the 
calling routine is concerned. The value assigned to the variable will be the 
last value assigned from within the rest of the program that addressed the 
main declaration. The result may not be what you expected. 

When do we use a local variable or a global one? Good programming 
practice dictates that, whenever possible, you use a local variable instead of 
a global one. Why? This is a practical question, since our first example used 
global variables. However, a global variable can sometimes wreck a good 
program. The reason is simple. It’s wise to use a function to complete its 
task, and then go about your business with the rest of the calling routine. In 
a large program, you may wish to use the same variable used within a func¬ 
tion again. A global variable retains its value, and can produce undesirable 
side effects later in your program. A local variable, however, has no effect 
on main program and can be used over and over again without fear of un¬ 
expected results. 

Perhaps the best reason to use a local variable in a function is the flex¬ 
ibility when making multiple calls to the same routine. Each time a call to a 
function is made in such a case (again, assuming no static variables), the 
variables will be starting from scratch. If global variables were used, the 
values left over from the previous execution of the function would still be in 
effect. 

Regardless of the reason, most experienced C programmers agree that 
unnecessary global variables often lead to programming bugs. Such bugs are 
difficult to find even for the most seasoned programmer. As a rule of thumb, 
you should try to use local variables whenever possible to avoid the problems 
outlined above. 


77 



Learning C on the ST 


We just made a case for using local variables, but we haven’t defined 
the method for exchanging information to and from a function when local 
variables are used. To do this, we use a mechanism called a parameter. 


Value Parameters 

Calling a function using variable parameters is slightly different than calling 
a function without them. Suppose we want to pass the values of the variables 
numl and num2 from the main routine to the routines add() and mult(). 
Our program would look like this: 

/♦Now we are passing parameters by value*/ 

#include (stdio.h) 

main() 

{ 

float numl, num2; 

printf("Enter a number: "); 
scanf("%f",&num1); 
printf("Enter another number: "); 
scanf("%f",&num2); 

printf<"\n\nWe are now computing the addition . . . \n”); 
add(num1, num2); 

printf{"\n\nWe are now computing the multiplication . . . \n"); 
mult(num1, num2); 

printf("\n\nThe program is finished!"); 
getchar{); 

} 

add(x, y) 
float x, y; 

{ 

float sum; 
sum = x + y; 

printf("The sum of your numbers is %f.\n", sum); 

} 


mult{x, y) 













C Functions 


float x, y; 

{ 

float product; 

product = x * y; 

printf("The product of your numbers is %f\n", product); 

} 

In our new program, you should notice that the variables numl and 
num2 are now local to and accessible only by main(). Also, the functions 
add( ) and mult( ) now have parameters associated with them in both their 
calls and definitions. For instance, 

add(x, y); 

declares add() as a routine with two parameters, x and y, whose type are 
float as defined by the next statement: 

float x, y; 

Whenever you declare a function to have parameters, you must define them 
after this declaration. 

Now when we invoke add(), we place the values of numl and num2 
within the parentheses in the call. This is identical to the way we have been 
calling printf and scanf all along; we simply place the parameters in the order 
in which the function expects them. The same rules outlined for add( ) apply 
to the function mult(). You should also notice that the variables sum and 
product are no longer declared globally either. They are now local variables 
to the routines in which they are used. That is, now only mult() can access 
the variable product, and add() is the only function that can access the vari¬ 
able sum. 

We refer to this method of parameter passing as passing by “value,” 
primarily because the parameters that are passed may be changed in the called 
function, but the calling function will not see any change. For instance, if 
we were to change x or y in add() or mult(), only these local versions of 
the parameters numl and num2 would be affected. The actual parameters 
numl and num2 would remain unchanged. Later we will see a method of 
changing parameter values. But first let’s look at how a function can return 
a single value. 


79 





Learning C on the ST 


The Return Statement 


A function may be set up to return one value to its calling function via the 
return statement. The return statement’s syntax is: 

return(value); 

where the value in parentheses is returned to the calling routine. For example, 
try this new version of our mathematics program: 

/*Now we are using the return statement.*/ 

#include (stdio.h) 

float numl, num2, sum, product; 
float mult(), add(); 

main() 

{ 

printf("Enter a number: "); 
scanf("%f",&num1); 
printf("Enter another number: "); 
scanf("%f",&num2); 

printf("\n\nWe are now computing the addition . . . \n"); 
sum - add(); 

printf("The sum of your numbers is: %f.\n" r sum); 
printf{"\n\nWe are now computing the multiplication . . . \n”); 
product = mult(); 

printf("The product of your numbers is: %f.\n", product); 

printf("\n\nThe program is finished!"); 

getcharO; 

} 

float add() 

{ 

float x; 

x - numl + num2; 
return(x); 

} 

float mult() 


80 











C Functions 


{ 

float x; 

x = numl * num2; 
return(x); 

} 

You should first of all notice that we made numl, num2, sum, and 
product global again, and our next declaration is something new. We have 
declared our function add() and mult() to be of type float. This simply means 
that they will be returning a value (via the return statement) of type float. 
The return value of a function defaults to int, and we would not have had to 
make these declarations if add() and mult() were returning int values. 
Nevertheless, it is good practice always to declare your functions like this 
even if they are returning an int value. 

The first several statements of main( ) are unchanged until the statement: 

sum = add{); 

which means that the function add() is invoked, and the value it returns is 
to be placed in the variable sum. Looking at add(), the local variable x is 
declared, and the sum of numl and num2 is placed in this local variable and 
is returned to main( ) by the statement: 

return(x); 

It’s as simple as that! The routine mult( ) is invoked and a value returned in 
the same manner as add(). The variables sum and product are then used in 
printf statements after the invocations of add() and mult(), respectively. 

The return statement is not only responsible for sending a value back 
to the calling routine, its execution also terminates its routine’s execution. 
For example, if we were to change add() to look like this: 

float add(); 

{ 

float x; 

x = numl + num2; 
return(x); 

printf("Hi thereAn"); 

} 


81 






Learning C on the ST 


the printf statement would never be executed. As soon as the return state¬ 
ment is reached, no other statements within that function will execute. This 
can be advantageous to the programmer who wants to exit routines from sev¬ 
eral different points, but in general, it is analogous to the goto statement in 
terms of unconditionally jumping out of a piece of code. And you know how 
we feel about gotos! As a final note on return, the value portion of the general 
syntax is optional. So, you could use a return statement that looks like this: 

return; 

and returns no value but does cause control to be passed back to the calling 
function. 

Now that we have seen how to pass information by value and with the 
return statement, let’s look at how to pass parameters by address. 


Variable Parameters 


When we pass a variable parameter in a function, it is usually just a copy of 
that variable, and the calling function may not use this to change the value 
of the actual variable. We say “usually” because all variables except arrays 
(i.e., strings) are passed by value unless they are preceded by the address 
operator &. We have already seen this operator used in scanf statements 
when we are inputting certain values. These certain values that require the 
& in scanf are, not coincidentally, any variable except arrays. When a string 
variable (which is the only array type we know so far) is passed to a function 
(such as scanf), a pointer to the beginning of it is passed, not the actual string. 
This is done automatically by C with no intervention on your part. Because 
of this, the called function is able to modify the original variable, since it 
knows exactly where it resides in memory. All other parameters are not passed 
as addresses by default. However, their addresses may be passed by using 
the & operator if it is desired. Consider our final version of the mathematics 
program: 

/*Now we are using pointers to variables.*/ *• 

#inc!ude (stdio.h) 

main() 


82 









C Functions 


float numl, num2, sum, product; 
printff'Enter a number: "); 
scanf{"%f",&num1); 
printff'Enter another number: "); 
scanf("%f'',&num2); 

printf("\n\nWe are now computing the addition . . . \n"); 
add(num1, num2, &sum); 

printf("The sum of the numbers is: %f.\n", sum); 
printf("\n\nWe are now computing the multiplication . . . \n"); 
mult(num1, num2, &product); 

printff'The product of the numbers is: %f.\n", product); 
printf("\n\nThe program is finished!"); 
getchar( ); 

} 

add(x, y, z) 
float x, y, *z; 

{ 

*z = x + y; 

} 

mult(x, y, z) 
float x, y, *z; 

{ 

*z = x * y; 

} 

Again, there are no new items in the program until we reach the in¬ 
vocation for the add function: 

add(num1, num2, &sum); 


This is read to say that “add is invoked and the values of numl and num2 
are passed along with the address of the variable sum.” The declaration of 
add() shows that it has three parameters: x, y, and z. These parameters are 
then defined (just as they were in our value parameter example) by specifying 
their type and names. However, the last variable has an asterisk in front of 
it. Why? Because this parameter is a pointer and * is the pointer operator. 
You would read this declaration as “add is a function with three parameters: 
x and y are float values, and z is a pointer to a float value.” Remember that 
this mysterious third parameter has an address in it and is therefore called a 


83 




Learning C on the ST 


pointer to a value. With this address, we are able to take the sum of x and 
y and put the result where z points. That is the meaning of: 

*z = x + y; 

In short, z points to the variable sum, so all reference to *z are actually 
references to sum itself. The concepts behind the mult() function are iden¬ 
tical to those of add(), except that z now points to the variable product. 

In summary, you just completed a very important chapter on C pro¬ 
gramming. You learned how to define and to write a function properly. You 
now know the difference between a global variable and a local variable. You 
learned how to pass information using value and address parameters using 
pointers. Lastly, you were also introduced to the return statement. Take a 
few minutes, give yourself a pat on the back, and review what you have 
learned. You’ll discover you can write some interesting programs with what 
you have learned thus far. 

Glossary for Review 

modular programming—the breaking down of various tasks within a 
program into individual functions as opposed to one large routine 

top-down design—a design where the resulting program has one main 
driving routine that calls other routines to perform specific tasks 

scope rules—the rules governing which functions can access which 
variables (i.e., global, local) 

Quiz -■> 


1. What is the difference between global and local variables? 

2. What does this declare: 

int *number; ? 

3. What is the difference between passing values by value and passing ad¬ 
dresses of values? 


84 






Library Features 


In this chapter, you will learn: 

• What the library concept is. 

• How to use the available library routines for mathematics, strings, 
etc. 

• How to use the numerous GEM library routines. 

• How to create a window in C. 


i 








Learning C on the ST 


How to Avoid Reinventing the Wheel 


When you begin writing your own programs, you soon realize that it would 
be nice if the computer could magically perform some common tasks that 
you use in different programs. For example, you might want to write a routine 
that would allow you to read in a string containing multiple words. The stan¬ 
dard scanf function will simply truncate all the data entered after a “white 
space” (e.g., tab, space, etc.), so you must have some method of not ter¬ 
minating the input when a white space is encountered. One way would be to 
use the getch( function repeatedly until a new line is input. Each successive 
character could be placed into a character string, and the new line would 
signal us to place a ‘\0’ terminator at the end of the string. 

It may have only taken you a few minutes to develop a function like 
this, but if someone had already written one for you, this would be a waste 
of time. In fact, there is a function available to you in the Standard I/O C 
library that is very similar to our description. But before we discuss it and 
several other library routines, let’s first take a closer look at what is meant 
by a library. 


The Central Library 


If you’re an avid book reader, you have come to learn that your local library 
has a wealth of books on topics from A to Z. And you've learned that by 
taking advantage of this library’s offerings you may have saved yourself 
hundreds of dollars by not having to purchase these books yourself. The C 
libraries are very similar to your local library in that they hold a wealth of 
functions enabling you to perform various tasks quickly, from complex math¬ 
ematics to high-resolution graphics. Because of this, the C libraries can save 
you time ( just as your library has saved you money), since you don’t have 
to write these commonly used routines yourself. All you need to do is prop¬ 
erly invoke them and follow a couple of simple rules: 1) if the library routine 
returns a value, make sure that the variable you are assigning to its value is 
compatible with what the function returns; and 2) if the routine passes pa¬ 
rameters, make certain that the order of your parameters is identical to that 
of the routine and that their types and sizes (e.g., long, short) match. 

Once you have an understanding of the routines, all you need to do is 


86 


« 









Library Features 


use the #include directive to let the compiler know you will be using that 
particular library and reference them as if they were your own. The remainder 
of this chapter is dedicated to presenting some of the more popular routines 
and macros available, with a brief discussion of each. 


Standard C Functions 


Mathematical 

The Cosine Function. This is a trigonometric function available to you that 
returns the cosine of the angle expressed in radians with this format: 


result = cos(angle); 

where both result and angle are declared as double. 

The Sine Function. This is another of the trigonometric functions available 
to you that returns the sine of an angle expressed in radians with the following 
format: 

result = sin(angle); 

again, where both result and angle are double. 

The Tangent Function. Again, this is a trigonometric function in which the 
result and the angle are double with the following format: 

result = tanlangle}; 

The Square Root Function. Use this function to determine the square root 
of a numeric value (i.e., the square root of 4 is 2) with this format: 

result = sqrt(number); 

where both result and number are double. 

The ASCII-to-Integer Function. If for some reason you would desire to read 


87 






Learning C on the ST 


in a number as a string and then convert it to an integer, this function will 
do the job: 

number = atoi(string); 

where number is an integer, and string may either be the name of a character 
array or a pointer to a character string. The reason for this leniency for string’s 
type is that a character array’s name is viewed internally by the compiler as 
a pointer to the first element of the array. 

String 

The gets Function . Up to now the only way we know of to read in a char¬ 
acter string is with the scanf function like this: 

scanf("%s",&a_string); 

A serious shortcoming of this method is the fact that any white space in the 
input string is viewed by scanf as the end of the string. Therefore it and all 
the characters following it are ignored. In short, scanf keeps reading in char¬ 
acters until it encounters either a white space or a new line and then replaces 
that character with the null terminator (‘\0’). The easiest way of avoiding this 
shortcoming of scanf is to use the gets function with the following format: 

gets(a_string); 

The gets function will allow you to read in, say, a last and first name and 
place them both in the same string variable without any further manipulation. 
[NOTE: For Lattice C users, a problem with gets was discovered by the 
author in an early version (3.03.03). If you used a gets after a scanf in a 
program, the gets would usually be ignored as if the input buffer was not 
completely cleared out after the scanf. A null value would then be placed in 
the variable passed to gets, resulting in a variable that never held much of 
anything! This problem has been reported to Metacomco, who said they would 
look into it.j 

The strcat Function. The ability to concatenate strings, or place one string 
at the end of another, is handled by the strcat function: 

strcat(a_string,b_string); 


88 







T 


Library Features _ 

where a_string and b_string are both pointers to strings. The b_string is 
concatenated to the a_string so that, if a_string contained ‘‘not" and b_string 
contained “hing” before the call to strcat, the value of a_string after strcat 
would be “nothing.” Notice that there are no spaces implicit in the call to 
strcat, so if you concatenate a first and last name, be sure to have a space 
either at the end of the first name or at the beginning of the last name before 
calling strcat. 

The strcmp Function. In order to compare one string to another, you cannot 
simply compare the string names like this: 

if (a_string == b_string) 

because this compares two unique addresses and will never be true (remember 
again that the name of a string simply represents the address of the first char¬ 
acter of the string!). The strcmp function may be used instead: 

strcmp(a_string,b_string); 

This function will return a value of zero if the strings are equal, a negative 
number if a_string is less than b_string, or a positive number if b_string 
is less than a_string. Be sure to use this function always when comparing 
strings: If you see an “if” statement like the one above, don’t be surprised 
if it is always evaluated as being false. 

The strlen Function. In other languages, the length of a string is specified 
in the first one or two bytes of the string so that the end point is known. C 
does not need this format, since it uses the null terminator to signify the end 
of a string. Sometime, however, you might need to know the length of a 
particular string, and rather than write a routine that will count characters 
until a null byte is found, you could use the strlen function: 

strlen(a_string); 

This function returns an integer value that represents the length of the string 
up to but not including the null terminator. 

Macros 

Also available to the C programmer are a number of very useful macros. The 
following is a list of several of these, along with a description of each: 


89 







Learning C on the ST 


isalpha(i) —this macro will return a nonzero value if i is an alphabetic char¬ 
acter (e.g., A through Z or a through z); a zero will be returned oth¬ 
erwise. 

isupper(i) —a nonzero value is returned if i is an uppercase character (e.g., 
A through Z); a zero is returned otherwise. 
islower(i) —a nonzero value is returned if i is a lowercase character (e.g., a 
through z); a zero is returned otherwise. 
isdigit(i) —a nonzero value is returned if i is a numeric character (e.g., 0 
through 9); a zero is returned otherwise. 
isspace(i) —a nonzero value is returned if i is a white space character (e.g., 
space, tab, etc.); a zero is returned otherwise. 
isalnum(i) —a nonzero value is returned if i is either an alphabetic or numeric 
character (i.e., fitting the criteria of either isalpha(i) or isdigit(i), above); 
a zero is returned otherwise. 

isprint(i) —a nonzero value is returned if i is a printable character; a zero is 
returned otherwise. 

iscntrl(i) —a nonzero value is returned if i is a control character; a zero is 
returned otherwise. 

isascii(i) —a nonzero value is returned if i is an ASCII character (decimal 
values 0 through 127); a zero is returned otherwise. 
toupper(i) —if i is a lowercase character, it is converted to uppercase. 
tolower(i) —if i is an uppercase character, it is converted to lowercase. 
abs(i) —the absolute value or magnitude of i is returned (e.g., abs(9) is 9, 
abs(-9) is 9, etc.). 


GEM Functions 


Included with your C compiler is a fairly extensive interface to the GEM, or 
Graphics Environment Manager, routines, which enable you to make your 
applications easily run like other programmers’ in both look and feel. The 
Megamax C package has descriptions of each of the GEM and AES routines 
that they support. These descriptions include explanations of the routines as 
well as fairly thorough explanations of the parameters that are passed to them. 

As a C programmer on the ST, you will probably never use most of the 
routines contained in this GEM library, but for when you do, if you are using 
Megamax C you need not worry about having to pull in special libraries; the 










Library Features 


shell takes care of this for you by checking all the libraries on your System 
disk. If you are using another compiler, however, say, Lattice C, all you 
need to do is use the #include directive for the gemlib.h file (if you intend 
to use any of the constants within it), change your linkage control file to 
LIBRARY the gemlib.bin file, and call the routine just as you would any 
other function (see the description of the linkage control file in your Lattice 
C manual). Be extra careful in the sizes of the parameters that you pass any 
of these routines! This is a rule that should always be followed no matter 
which library you are working with. But remember, if the routine is looking 
for a short or a word parameter, don’t pass it an int or a long. 

The remainder of this section is a description of a few GEM routines 
that are either necessary or very useful for your applications. Take note that 
if you are wishing to learn GEM graphics routines, they will be covered in 
Chapter 10; these routines are more general purpose ones that everyone should 
be interested in. 

appl-init() Before you can call any other GEM routines in your program, 
you must call the appl_init function. The basic premise behind this is initially 
to identify yourself. Appl.init is also used to allow multitasking, or the run¬ 
ning of more than one application at once. For this reason, appl_init will 
return a long value that represents your application identification number as 
follows: 

my_id = appl_init(); 

If my_id is not greater than zero, the function did not work. For our pur¬ 
poses, it is not necessary to look at the value returned from appl init. 

graf-handle() The next step in initializing your program is to get a chunk 
of memory that will contain all the attributes of the application. The 
graf-handle routine will return a pointer to this area that later may be used 
as a parameter to other routines specifying our application. We have always 
used graf_handle like this: 

handle = graf-handlel&dummy^dummy^dummy^dummy); 

The four parameters represent character attributes that we do not wish to 
change from the default values. Now that we have a handle to call our own, 
we can use it to open our own workstation and set more attributes. 


91 







Learning C on the ST 


v-opnvwk() The v_opnvwk function is invoked like this: 

v_opnvwMwork_in,&work_stn_hndle,work_out); 

where work-in is an 11 -element array containing information such as line 
type, text color, fill type, etc.; &work_stn_hndle is the address of our han¬ 
dle from graf_handle; and work_out is an array that is filled in by v_opnvwk 
and contains advanced information that we don’t use in this book, such as 
raster information. We have always initialized the work-in array to 10 ones 
and a 2 as follows: 

for (loop = 0; loop < 10; loop+ + ) work_in[loopl = 1; 
work-in[10j = 2; 

This is a fairly standard way of setting up these attributes and should suffice 
for your programs. 

As was mentioned, we don’t ever look at the work_out array, but, of 
course, it must be present in your call to v_opnvwk. Finally, we can see the 
first use of our work_stn-hndle from graf_handle here as it specifies our 
application to GEM. 

v_clsvwk() Once we are finished with our application, we must call v-dsvwk 
in order to close our workstation. The function has one parameter, the handle 
to our workstation, and is called as follows: 

v_clsvwk(work_stn_hndle); 

appl_exit() The last task to be completed in a simple program is to tell 
GEM that we are finished by calling appl_exit: 

appl_exit<); 

This lets GEM know that the application will now be terminated and any 
overhead variable space can be released. 

graf-mouse() When you want to change the characteristics of the mouse, 
graf_mouse may be called: 

graf_mouse(shape,&nothing); 


92 






Library Features 


where shape is a long value that defines the mouse form, and nothing is an 
address of a custom mouse’s shape’s description in memory. We have used 
graf_mouse to turn the mouse’s display on and off using two of the constants 
defined in the gemlib.h file: M_OFF and M_ON. There are several other 
constants that you can use to change the mouse, such as ARROW (the fa¬ 
miliar arrow pointer), TEXT_CRSR (the I-Bar form used in certain word 
processors and editors), etc. If the USER_DEF constant is used, graf_mou.se 
will go to the address of nothing and use the values there to build a user- 
defined mouse form. 

v-clrwk() If you want to clear the screen in your C program, call v_clrwk 
as follows: 

v_clrwk(work_stn_hndle); 

Again, we need to use the work_stn_hndle in order to specify which work¬ 
station we are referring to. However, a call to v_clrwk is not enough to clear 
the screen in the traditional sense; the cursor is still located in its last position, 
so we also need to move the cursor to the home position with v_curhome. 

v-curhome() After calling v_clrwk, v_curhome should be invoked to move 
the cursor to the home position: 

v_curhome(work_stn_hnd!e); 

The combination of these two routines will clear the screen and place the 
cursor in the home position much like a clearing routine in other languages 
(e.g., CLS in many forms of BASIC). [NOTE: As of release 1.0 of Megamax 
C, their v_curhome is not implemented; perhaps Megamax will be including 
this function in release 2.0.] 


Windows 

Probably the most noticeable feature of the GEM interface for the Atari 520 
ST is windowing. The ability to manipulate multiple windows simultaneously 
on the screen is certainly an attribute that adds to the user-friendliness of the 
system. However, if you’re used to working with functions to control win¬ 
dows on, say, the Apple Macintosh, you'll be greatly disappointed by the 
amount of code necessary simply to create a window, let alone direct I/O to 


93 






Learning C on the ST 


it; while it requires only a handful of instructions to display and write to a 
window on the Macintosh, it takes several dozen instructions to do the same 
on the ST. As you have seen in the program examples up to this point, it's 
not necessary to work with windows at all on the ST. For this reason, and 
the fact that an entire book could be written on window handling, we will 
simply show you how to create and display a window in C. 

The following is a C listing that, when compiled, linked, and executed 
with a PRG extension, will display the outline of a window with comer co¬ 
ordinates (50,50), (550,50), (50,300), and (550,300). The window will be 
displayed until the RETURN key is pressed on the keyboard. 

/•Window Display Program*/ 

#include (stdio.h) 

#include (gembind.h) 

int wind_handle; /*pointer to our window*/ 
int work_stn_hndle,handle; 

int contrl[12], intinll28], ptsin[128], intout[128], ptsout[1281; 

main() 

{ 

int nothing; 

window_set_up(); 
graf-mouse{M_OFF,&nothing); 

getchar(); /*wait for user to press RETURN to quit*/ 

graf_mouse(M_ON,&nothing); 

clean_up(); 

I 

window_set_up() 

{ 

short loop; 
int work_in[11]; 
int work_out[57]; 
intt dummy; 
int x_loc = 50; 
int y_loc = 50; 
int height = 250; 
int width = 500; 

appl_init(); 

handle = graf_handle(&dummy / &dummy,&dummy,&dummy); 
work_stn_hndle = handle; 


94 








Library Features 


for (loop = 0; loop < 10; loop+ + ) work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

wind.handle = wind-create(0x02p<Joc,y_loc,width,height); /*get 

handle*/ 

wind_open(wind_handle,x_loc,y_loc,width,height); /* open the 
window.*/ 

} 

clean_up{) 

wind_close(wind_handle); /*close the window*/ 
wind_delete(wind_handle); /*get rid of the window*/ 
v_clsvwk(work_stn_hndle); 
appl_exit( >; 

} 

You should pay close attention to this program’s format because it is 
the way the remaining chapters in this book are laid out. In order to access 
many of GEM’s abilities, we use a setup routine that invokes appl_init( ) 
and v_opnvwk(), which were described above. In addition, when we are 
finished with our program, we call a clean_up( ) routine that closes our work¬ 
station via v_clsvwk( ) and ends the application via appl_exit( ). After the 
program initialization, a call to the gemlib routine wind_create is made as 
follows: 

wind_handle = wind_create(0x02,x_loc,y_loc,width, height); 

where 0x02 is the value passed for the window flags, x_Ioc and y_loc are 
the coordinates of the upper left-hand comer of the window, and width and 
height specify how wide and high the window extends. These points are 
based on the screen’s monochrome coordinate system, which has 640 points 
horizontally and 400 points vertically. Each point is also commonly referred 
to as a picture element, or pixel. So there are 256,000 pixels in monochrome 
mode. 

The possible flags that may be set include the following bit positions: 

0 title bar and name 

1 symbol to close 

2 symbol to make the window full-size 

3 symbol to move the window 


95 





Learning C on the ST 


4 information line 

5 symbol to resize the window 

6 up arrow symbol 

7 down arrow symbol 

8 vertical slider 

9 left arrow symbol 

10 right arrow symbol 

11 horizontal slider 

In our example, only bit number 1 is set, so that the only extra feature of 
the window is a close symbol. The previously initialized variables x_ioc, 
y_loc, height, and width are also passed to wind_create to specify the max¬ 
imum size of the window. A handle for the window is returned by wind_create 
and placed in the variable wind-handle. This handle, similar to our familiar 
work_stn_hndle, is simply a pointer to the information that describes our 
window; it must be used in any further calls to window routines to specify 
this window. 

Next, the wind_open routine is invoked to display the window. The 
parameters passed to it are the wind-handle (which was set by wind_create, 
above), the coordinates for the top left-hand comer of the window, as well 
as its width and height. For simplicity, we used the same values for the last 
four parameters that we used in wind_create; but you could have used dif¬ 
ferent values, which would have resulted in a smaller window size. 

The getchar() routine is then called to await the depression of RE¬ 
TURN, and finally the clean_up() routine is invoked First to close the win¬ 
dow via the wind_close function and then to delete the window with 
wind_delete. Each of these routines takes the window handle as an argument 
in order to know which window you want to close and delete. Once you close 
a window you may open it again later, but once you have deleted the window, 
its attributes no longer exist in memory. 

You might have noticed that we declared five int variables that we never 
used in the line: 

int contrl[12], intin{128], ptsin[128], intout[128], ptsoutf128]; 

These variables are used in some of the VDI routines on the ST. If you are 
using any VDI routines, these variables must be declared in your program. 
You don’t need to know what they are responsible for, but you will run into 
linkage errors on Megamax C if you do not include them. 






Library Features 


As was mentioned above, this is a fairly simple look at windowing on 
the ST, and you could easily develop very sophisticated applications without 
ever even having to use windows at all. However, if you do need more in¬ 
formation on ST window routines, we suggest that you take a look at the 
Atari ST GEM Programmer’s Reference Guide from Abacus Software; this 
book goes into painful detail on advanced window concepts as well as event 
handling, which is a must for any serious window application. 

Glossary for Review 

library —a central gathering of commonly used routines; the various C 
libraries (e.g., gemlib.bin) have routines for mathematics, string manip¬ 
ulation, graphics, etc. 

pixels —the tiny dots that make up the screen of a computer display; the 
ST has about 256,000 of them! 


Quiz 


1. What are the results of the following functions/macros? 

A. strcat(“Ata”,“ri”); B. sqrt(25); 

C. abs(12); D. is!ower(‘a’); 

E. strcmp(“Joe’V‘Joe”); F. isspace(‘3’); 


2. What happens if v_clrwk is not followed by a call to v_curhome? 

3. What is the difference between the x_loc, y_loc, width, and height 
parameters in wind-create and wind_open? 


97 
















y 


In this chapter, you will learn: 

• How complete C programs operate on the Atari ST. 

• How to modify existing programs to suit your particular needs 
better. 

• Why comments are such a critical part of programming. 

• The importance of proper error handling. 

• Four programs dealing with metric conversions, guessing num¬ 
bers, hexadecimal notation, and album-to-cassette length calcu¬ 
lation, which are all documented with line-by-line and variable ex¬ 
planations. 






Learning C on the ST 


The Entire Picture: Complete C 
Programs 


Now that you have learned most of the individual elements of C program¬ 
ming, it is time to put everything together and discuss a few application pro¬ 
grams. Each of these programs has been selected and designed for the purpose 
of tying together all the topics that have already been discussed, as well as 
preparing you for further study in the areas of file handling, graphics, re¬ 
cursion, and arrays. Let’s first take a look at a program that will perform 
various conversions to the metric system. 


Metric Conversions Program 


Enter the following program into your ST while paying close attention to 
spacing within quotes and the use of special characters (e.g., &, #, etc.): 

/♦Metric Conversion Program*/ 

#include (stdio.h) 

#include (gembind.h) 

#define METER_CONV 39.37 /*conversion constant of inch to 
meter*/ 

#define LITER_CONV 61.02 /*conversion constant of cub. inch to 
meter*/ 

#define GRAM_CONV 0.035 /*conversion constant of ounces to 
grams*/ 

short work_stn_hndle,handle; 
int nothing; 

int contrlfl2], intin[128], ptsin[128], intout[128], ptsout[128]; 

main() 

{ 

int done; /*are we finished yet?*/ 

int valid; /*is this valid input?*/ 

char choice; /*user's choice from menu*/ 


100 








Programmer’s Corner 


window_set_up(); 
graf_mouse(M_OFF,&nothing); 
done = FALSE; 
while (Idone) 

{ 

valid = FALSE; 
while (Ivalid) 

{ 

printf(''Which do you wish to do:\n\n"); 
printf(" 1. Convert from inches to meters\n"); 
printf(" 2. Convert from cubic inches to liters\n"); 
printf(" 3. Convert from ounces to grams\n”); 
printf(" 4. QUIT\n\n"); 
printf("PLEASE ENTER 1, 2 , 3 OR 4"); 
switch (chice = getcharO) 

{ 

case "T: inch_to_meter(}; 
valid = TRUE; 
break; 

case '2': cub_inch_to Jiter(); 
valid = TRUE; 
break; 

case '3': ounce_to_gram(); 
valid = TRUE; 
break; 

case '4': valid — TRUE; 

done = TRUE; 
break; 

default: printf(''\nlNVALID RESPONSE . . . PLEASE TRY 
AGAIN !\n\n"); 

} 

} 

} 

graf_mouse(M_ON,&nothing); 
clean_up(); 

} 

window_set_up{) 

{ 

short loop; 
int work_in[11]; 
int work_out[57]; 
int dummy; 


101 








Learning C on the ST 


appl_init(); 

handle = graf_handle(&dummy,&dummy,&dummy,&dummy); 
work_stn_handle = handle; 

for (loop = 0; loop < 10; loop+ + ) workJnlloop] = 1; 
work_in[10] = 2; 

v_opnvwk{work_in,&work_stn_hndle / work_out>; 

} 

clean_up{) 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit{); 

} 

inch_to_meter( > 

{ 

float inches; /*how many inches to express in meters*/ 
float meters; /*how many meters*/ 

printf("\n\nHow many inches? "); 

scanf("%f",&inches); 

meters = inches / METER_CONV; 

printf("\n%8.2f inches equals %8.2f meters.\n\n",inches,meters); 

} 

cub_inch_to_literU 

{ 

float cubic_inches; /*how many cubic inches*/ 
float liters; /*how many liters*/ 

printf{"\n\nHow many cubic inches? "); 

scanf("%f",&cubic_inches); 

liters = cubic-inches / LlTER_CONV; 

printf{"\n%8.2f cubic inches equals %8.2f liters.\n\n" 

,cubic_inches,liters); 

} 

ounce_to_grams() 

{ 

float ounces; /*how many ounces*/ 

float grams; /*how many grams*/ 

printf{"\n\nHow many ounces? "); 

scanf("%f",&ounces); 

grams = ounces / GRAM_CONV; 

printf("\n%8.2f ounces equals %8.2f grams.\n\n",ounces, grams); 








Programmer's Corner 


This program will allow you to convert inches to meters, cubic inches 
to liters, and ounces to grams. You may continue to make numerous con¬ 
versions without having to rerun the program, and the modular nature of the 
conversion procedures allow you to add more options easily. 

Program Explanation 

We begin, of course, with our program name as a comment. The #include 
directive is then used to include the stdio.h and gembind.h header files. 
Next, the #define directive is used to set up constants for our conversions. 
The global variables are then declared and immediately thereafter are the 
blocks containing the main routine, our usual setup and cleanup routines, and 
three conversion routines: inch_to_meter, cub_inch_to_liter, and 
ounce.to_gram. Now let’s look at each block in detail in order to determine 
exactly how they fit together. 

The main program block begins by declaring a done variable to indicate 
when the user is finished performing conversions, a valid variable to indicate 
whether the user’s input is valid, and a choice variable to hold the user’s 
input. The window_set_up graf.mouse routines are then called to set up 
the program. Each of these procedures has already been explained and should 
warrant no further discussion, other than mentioning that it is indeed good 
practice to develop a series of functions that you will use at the beginning 
of your interactive programs so that they are consistent; otherwise your pro¬ 
grams may look rather shabby and be difficult to debug. 

Immediately after this initialization, the done is set to FALSE, and we 
begin the main loop with a while (!done) condition. The valid is initialized 
to FALSE, and a nested while (Ivalid) loop is started that will be directly 
affected by valid input from the user. A menu is then displayed that shows 
four options for the user to choose to continue. As shown in the successive 
printf statements, the options include each of the three conversions and the 
option to quit the program. A getchar is then executed, and whatever char¬ 
acter the user entered at the menu is then used in a switch statement to de¬ 
termine the appropriate action. As the switch statement reads, if the user 
entered a 1, 2, or 3, then the appropriate procedure is invoked, and valid is 
set to TRUE because the user’s response was acceptable. If a 4 was entered, 
both variables valid and done are set to TRUE because the user wishes to 
quit, and the response was acceptable. The default clause of the switch state¬ 
ment is then used to display an error message to the user, and no variables 
(flags) are changed. 

When you now look back up at the two previous while loops, it should 


103 




Learning C on the ST 


be obvious that the outer one (while (!done)) terminates only when a 4 is 
entered, whereas the inner one (while (!valid)) terminates when any value 
from 1 to 4 is entered. 

The three procedures are quite similar in that they all request an amount 
of some type (inches, cubic inches, or ounces) and then convert that amount 
into its equivalent in the metric system (meters, liters, or grams) through the 
use of a conversion constant (METER_CONV, LITER_CONV, or 
GRAMLCONV). You may also have noticed that each of these three pro¬ 
cedures uses the formatted printf statement (for example, %8.2f), where the 
first digit (8) specifies the number of characters to be printed (field width), 
and the second digit (2) specifies how many digits will be displayed to the 
right of the decimal point. 

Be sure to save this program on your disk so that you may now look at 
the next program, which guesses a number in the least average number of 
attempts. 


Guess a Number Program 

This particular program was designed to guess a number you would choose 
from 1 to 100 in the least number of tries. In fact, we guarantee that it will 
require no more than seven guesses to determine your number! 

The secret behind the program is called a binary search, and it works 
as follows: 

1. first, a range is set up for possible values (in this case, I to 100); 

2. next, the user is asked to pick a number within that range; 

3. the midpoint of possible values is then calculated with the formula: 
(maximum value + minimum value) 2, which would yield 50 
((100 + 1) - 2 = 50); 

4. the program then asks you if your number is a) equal to this mid¬ 
point, b) greater than this midpoint, or c) less than this midpoint; 

5. if it is equal to your number, then the program stops; otherwise, if 
your number is greater, then the bottom half of possible values (1- 
50 for the first guess) are thrown out, and we make the midpoint (or 
guess) the lowest possible value (actually, midpoint + 1 is the lowest 
possible value, but due to integer truncation we can ignore the “ + 






Programmer’s Corner 


1”) and go back to step 3. If your number is less than the guess, 
then the top half of possible values (50-100 for the first guess) are 
thrown out, and we make the midpoint (or guess) the highest possible 
value and go back to step 3. 

This loop from step 5 to step 3 will continue until your number has 
correctly been identified and, with the given range of 1 to 100, will take no 
more than seven attempts. Seven is not some magical number that holds true 
for all ranges, however; rather, this figure is derived from determining the 
lowest power of 2 that exceeds the range size. To understand this concept 
more clearly, take a moment to review the following table of powers of 2: 

Power Expression Equivalent 

0 2 ° 1 

1 2 1 2 

2 2 2 4 

3 2* 8 

4 2 4 1 6 

5 2 5 32 

6 2 6 64 

7 2 7 128 

8 2 8 2 56 

9 2 9 512 

10 2 10 1024 

As you can see, the lowest power of 2 that results in a figure equal to 
or greater than our range size (100) is 7. Therefore, it will take at most seven 
guesses before the number is correctly picked. Also, the reason we raise 2 
to a particular power is because we are halving the list of possibilities with 
each guess. 

Now that you have an understanding of how the program works, enter 
it into your ST as it appears below: 

/•Number Guessing Program*/ 

#includ (stdio.h) 

#include (gembind.h) 


105 






Learning C on the ST 


#define MAX_GUESS 7 /^maximum guesses the program will make*/ 
#define MIN_START 1 /^initial min_num value*/ 

#define MAX_START 100 /^initial max.num value*/ 


int work_stn_hndle,handle; 
int nothing; 
char go_ahead; 
int found; 
int guess; 
int done; 
int max_num; 
int min_num; 
int count; 
int valid; 

main() 

{ 

window_set_up(); 
graf_mouse(IVL_OFF,&nothing); 
done = FALSE; 
while (Idone) 

{ 

min_num = MIN_START; /*set the range from 1 to 100*/ 
max_num = MAX_START; 

printf(''Pick a number between 1 and 100 . . . \n”}; 
printf("Press any key to continue . . . \n\n"); 
go_ahead = getcharO; 
found = FALSE; 

guess = (max_num + min_num)/2; 
count = 1; 

while (((found) && (count <= MAX_GUESS)) 

{ 

printf("\nls your number less than (L), greater than (G),"); 
printf("or equal to (E)%—3d?",guess}; /*left justify guess*/ 
switch (go_ahead = getcharO) 

{ 

case 'E': 

case 'e': found = TRUE; 

break; 
case 'L': 

case 'I': max_num = guess; 

guess = (max.num + min_num)/2; 

++count; 


/*input character from user*/ 

/*has your number been found?*/ 

/*the actual guess*/ 

/*are you finished with the game?*/ 

/*the current maximum range of guesses*/ 

/*the current minimum range of guesses*/ 

/*the number of guesses which have been made*/ 
/*did you provide a valid response?*/ 


106 





Programmer’s Corner __ 

break; 
case 'G': 

case 'g': min_num = guess; 

guess = (max_num + min_num)/2; 

+ +count; 
break; 

default: printf(''\nlNVALID RESPONSE . . . PLEASE TRY 
AGAIN !!\n"); 

} 

} 

if (count <= MAX_GUESS) 
printf("\nSee, we guessed your number in only %1d 
guesses!\n" / count); 
else 

{ 

printf("\n\nYou must have missed your number because the 
program\n"}; 

printf("has already made the maximum number of guesses!\n"); 

} 

valid = FALSE; 
while (Ivalid) 

{ 

printf("\nWould you like to play again (Y/N)?"); 
switch (go_ahead = getcharO) 

{ 

case 'Y': 

case valid = TRUE; 
break; 

case 'N': 

case 'n': done = TRUE; 
valid = TRUE; 
break; 

default: printf("\nlNVALID RESPONSE . . . PLEASE TRY AGAIN I''); 

} 

printf("\n\n"); 

} 

} 

graf_mouse(M_ON,&nothing); 
clean_up<); 

} 

window-set_up() 

{ 

short loop; 


107 



Learning C on the ST 


int work_in[11]; 
int work_out[57]; 
int dummy; 

appl_init{); 

handle = graf_handle(&dummy,&dumrny,&dummy,&dummy); 
work_stn_hndle = handle; 

for (loop = 0; loop < 10; loop+ + ) workJn[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit(); 

} 


Program Explanation 

This program contains no functions other than the main and initialization/ 
cleanup routines and is therefore easily read from top to bottom. After our 
constant and variable declarations, we begin again by setting up the screen 
for input/output with the user. 

After this screen initialization takes place, we begin our main loop (while 
(Idone)), which will continue until the user decides to stop playing. We im¬ 
mediately assign values to our min_num (1) and max_num (100) variables, 
and through the use of printf and getch we ask the user to pick a number 
between 1 and 100 and wait for any key to be pressed to continue. We then 
initialize found to FALSE and use the formula for finding the midpoint be¬ 
tween two numbers to determine our first guess (the formula simply adds our 
max_num and min_num figures and then divides the sum by 2 to arrive at 
an average, or midpoint, of the two numbers). The integer count is then in¬ 
itialized to 1 and through the next “while'’ loop (while (Ifound) and (count 
<= MAX_GUESS)) is not permitted to exceed 7. Rather than saying: 

while ((Ifound) && (count <= 7)) 


108 


we chose to assign a constant (MAX_GUESS) to represent 7 for two reasons: 
1) it makes the statement more readable, and 2) it lends itself to a more 
obvious change should anyone choose to modify the program to allow the 





Programmer’s Corner 


range of numbers to be 1 to 1000. (This is because, from the power of 2 
chart above, you would have to make the maximum number of guesses equal 
to 10, and you may forget what the 7 represented; but with the constant name 
MAX_GUESS, it should be more meaningful.) 

So we begin this “while” loop and continue to execute it until we either 
find the user’s number or we have made the maximum number of guesses. 
After starting this loop, the user is asked to say whether his/her number is 
less than, greater than, or equal to the calculated guess. A switch statement 
is then performed on the response and: 

1. if the guess is equal to the user’s number, found is set to TRUE so 
that the while((!found) && (count <= MAX_GUESS)) loop will 
terminate; 

2. if the user’s number is less than the guess, the top half of possible 
numbers is thrown away (by assigning max.num = guess) and a 
new guess is calculated; 

3. if the user’s number is greater than the guess, the bottom half of 
possible numbers is thrown away (by assigning min_num = guess) 
and a new guess is calculated; or 

4. if the user enters in an invalid response (the default clause), a mes¬ 
sage is displayed. 

[Note that both uppercase and lowercase letters are used in the switch state¬ 
ment, so that the user can have the Caps Lock key down or up and the input 
will be valid.] 

Next, when the program has either found the number or has guessed the 
maximum number of guesses, if the user’s number was guessed (meaning 
that count <= MAX_GUESS is TRUE), a message is displayed that says 
that it took only count guesses to determine it, or a message is displayed that 
tells the user that the maximum number of guesses has been made and since 
the number was not found, he/she must have missed it. When either of these 
events is complete, the other nested “while” loop (while (!valid)) is executed. 
This loop is used to ask the user if he/she would like to play again, and 
based on the user’s input, the flags valid (if a ‘Y’, ‘y’> ‘N’, or ‘n’ is entered) 
and done (if a ‘N’ or V is entered) are set to TRUE. As with other inputs 
of this nature, if an invalid response is entered (the default clause) then an 
error message is displayed. 


109 





Learning C on the ST _ 

Decimal to Hexadecimal Conversion Program 


The next program in this chapter may be used to convert any decimal (base 
10) integer value into its hexadecimal (base 16) equivalent. If you are familiar 
with hexadecimal notation, simply enter the program, and follow its instruc¬ 
tions to perform conversions. For those of you who are unfamiliar with hexa¬ 
decimal numbers, or “hex” as they are commonly referred to, the following 
is a brief explanation of their use and significance in the computer world. 

Since we were small children, we have used the base 10 numbering 
system; we simply use the digits 0 to 9 to represent any number (there are 
10 unique digits (0, 1, . . 8, 9) in this system). Let’s take a look at the 

number 425. What this number really represents is four hundreds, two tens, 
and five ones, because the first digit (5) is in the one’s position, the second 
digit (2) is in the ten’s position, and the third digit (4) is in the hundred’s 
position. If we multiply and add these figures together, we get: 

4 x 100 = 400 
2 x 10 - 20 
5xl= 5 

425 TOTAL 

Also note that since we are using base 10, each digit is multiplied by 
10 raised to its position minus 1, or mathematically: 

decimal place value = digit x 10 <pos,tlon_l) 

For instance, the second decimal place’s value is: 

2 x 10 <2-1> = 2 x 10 1 - 2 x 10 = 20 


as was stated above. 

This discussion may seem quite trivial, but now keep in mind that the 
same rules apply for hexadecimal numbers, except that now we have 16 unique 
digits, and each hexadecimal place in the number is multiplied by a power 
of 16. First of all, the 16 digits for hexadecimal notation are: 


110 







Programmer’s Corner 


Hex 

Decimal Eq 

$0 

0 

$1 

1 

$2 

2 

$3 

3 

$4 

4 

$5 

5 

$6 

6 

$7 

7 

$8 

8 

$9 

9 

$A 

10 

$B 

11 

$C 

12 

$D 

13 

SE 

14 

$F 

15 


As you can see, the digits 0 through 9 are equivalent in both systems, 
and the values 10 through 15 are represented by the first six letters in the 
alphabet. In addition, hex numbers are usually preceded by a dollar sign to 
differentiate them from a base 10 value. 

Now let’s look at the hex number $1A9. What is this equivalent to in 
base 10? If we use our rules from above, we would multiply and add to get 
the following: 


1 x 16 (3_,) = 1 x 16 2 = 256 
10 x 16 (2-n = 10 x 16' = 160 
9 x 16"“ l> = 9 x 16 (0> - 9 

425 TOTAL 

The hex values are represented in boldface to show how they fit into 
the computations; compare them to the base 10 values from our earlier ex¬ 
ample to understand the similarity figuring the total. (Also, recall that $A is 
equal to 10 in base 10.) 


Ill 



Learning C on the ST 


Now that you know how to translate a hex number into base 10, let’s 
see what’s involved in converting from decimal to hex; all you have to re¬ 
member is to divide by 16 and write the remainder. For example, to convert 
425 into hex we first divide it by 16: 

425 -5- 16 = 26 with a remainder of 9 
we then divide the resulting dividend (26) by 16: 

26 -T- 16 = 1 with a remainder of 10 
we then divide this resulting dividend (1) by 16: 

1 - 5 - 16 = 0 with a remainder of 1 

and, since our dividend is zero, we stop. We have determined that 1, 10, 
and 9 are the remainders (from last to first), and if we convert them to their 
respective hex equivalents (basically, only 10 must be converted to A), we 
find that the hex equivalent of 425 is $1A9. 

You now know how to go from decimal to hex and vice versa, but you 
may still be wondering how this knowledge could ever be useful. In computer 
applications, numbers are quite often expressed in systems other than base 
10, and hexadecimal notation is one of the most popular of these systems. 
Therefore, it is worth your while to try a few conversions on your own and 
check them by either converting back to the original system or by using the 
following program: 

/ # Hexadecimal Conversion Program*/ 

#include (stdio.h) 

#include (gembind.h) 

int work_stn_hndle,handle; 

main() 

{ 

int done; 
int valid; 
int number = 0; 
char answer; 
int nothing; 


112 








Programmer’s Corner 


window_set_up(); 
graf_mouse(M_OFF,&nothing); 
done = FALSE; 
while (Idone) 

{ 

printf("Please enter a decimal integer without commas or decimal 
point. "); 

scanf("%d",&number); 

printf("The hexadecimal equivalent of %d is %x\n'',number,number); 
valid = FALSE; 
while (!valid) 

{ 

printf("Would you like to try another number (Y/N)?"); 
answer = getcharO; 
switch (answer) 

{ 

case 'Y': 

case 'y': printf("\n"); 

valid = TRUE; 
break; 

case 'N': 

case 'n': valid = TRUE; 

done = TRUE; 
break; 

default:printf("\nlNVALID RESPONSE . . . PLEASE TRY 
AGAIN !\n"); 

} 

} 

} 

graf_mouse(M_ON,&nothing); 
clean_up(); 

} 

window_set_up{) 

{ 

short loop; 
int work_in[11]; 
int work_out[57]; 
int dummy; 

appl_init(); 

handle = graf_handle{&dummy,&durnmy,&dummy,&dummy); 
work_stn_hndle = handle; 

for (loop = 0; loop < 10; loop+ + ) work_in[loop] = 1; 


113 





Learning C on the ST 


work_in[10] = 2; 

v_opnvwk{work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 

appl_exit(); 

} 

Program Explanation 

This program is very simple in nature, thanks to C’s convenient conversion 
abilities in printf statements. 

After our usual initialization, the main “while" loop (while (Idone)) is 
begun, and the user is requested to enter a base 10 integer, which is then 
converted within the second printf statement. By using the %d control string, 
the number is displayed as a base 10 integer. But when we use the %\ control 
string, the number will be displayed as a hexadecimal number. Notice that 
we precede the hex value by the $ sign to signify that it is a base 16 value. 

This very simple conversion, which took place in one printf statement 
in C, might require a few dozen statements in, say, PASCAL, where you 
would have to read the number in and then look at it one digit at a time and 
convert it to a string so that the digits 10 through 15 (A through F) could be 
displayed. This is indeed a very nice feature of C, one that we will use again 
later in the book to create a program that will “dump" (or display) files in 
both hex and character form. 

After we have displayed the converted number, another “while" loop is 
started ((while (Ivalid)), which asks the user if he/she would like to convert 
another number. The input is received via getch, and a switch statement is 
used to determine the appropriate action. If an invalid response is made, the 
default option in the switch is again used to display an error message. Finally, 
the program ends with our familiar clean_up routine to shut down the ap¬ 
plication. 

Tape Counter Program 

If you have ever made any recordings of your phonograph albums, you are 
familiar with the problem of trying to figure out exactly how many songs 
you can get on one side of a tape. We have all spent some time adding 
minutes and seconds to try and get the most out of a blank tape. But if you 


114 






Programmer's Corner 


take a few moments to use the following program, your future tapes will be 
a snap to make. 

All you have to do is enter each song’s length in minutes and seconds, 
and the program will keep a running total of the amount of time necessary 
to make the recording. 


/*Tape Counter Program*/ 

#include (stdio.h) 

#include (gembind.h) 

int work_stn_hndle,handle; 

int minutes; /*song's number of minutes*/ 

int seconds; /*song's number of seconds*/ 

int tot_minutes; /*total minutes of all songs entered*/ 

int tot_seconds; /*total seconds of all songs entered*/ 

main() 

{ 

int nothing; 

window_set_up(); 
graf_mouse(M_OFF,&nothing); 
tot_minutes = 0; 
tot_seconds = 0; 
do 
{ 

printf("How many minutes is this song?''); 

scanf("%d'\8tminutes); 

if (minutes != 0) 

{ 

printf("How many seconds?''); 
scanf("%d”,&seconds); 
calc_and_disp{); 

} 

} while (minutes != 0); 
graf_mouse(M_ON,&nothing); 
clean_up(); 

} 

window_set-up() 

{ 

short loop; 
int work_in[11|; 


115 




Learning C on the ST 


int work_out[57]; 
int dummy; 

appl_init{); 

handle - graf_handle(&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle - handle; 

for (loop = 0; loop < 10; loop+ + );work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit(); 

} 

calc_and_disp() 

{ 

tot_minutes = tot_minutes + minutes + (tot_seconds + seconds}/60; 
tot_seconds = (tot_seconds + seconds) % 60; 
printf("\n\nTOTAL REQUIRED TIME SO FAR IS: %d MINUTES 
%d SECONDS\n\n", 

tot_minutes,tot_seconds); 

} 

Program Explanation 

The tape counter program has one main “do . . . while” loop, which con¬ 
tinues until the user enters a zero value for the number of minutes in a song. 
This loop first requests the number of minutes, and then if minutes is not 
zero, the number of seconds is requested. Then, calc_and_disp is called to 
figure the total number of minutes and seconds thus far. The tot_minutes 
figure is calculated by adding the previous tot_minutes to the latest song’s 
number of minutes. This amount is then added to the remainder after adding 
the tot_seconds to the latest song’s seconds and dividing that sum by 60 as 
follows; 

tot_minutes = tot_minutes + minutes + (tot_seconds + seconds)/ 
60; 


116 


The tot_seconds amount is then figured by using the remainder, or modulus, 
of adding the tot_seconds to the latest song’s seconds and dividing by 60. 






Programmer’s Corner 


Again, the % operator is used to determine the remainder of dividing X by 
Y (X % Y). 

The cumulative totals are then displayed by calc_and_disp, and control 
returns to main, where the “do . . . while” loop continues. The program 
ends with the clean_up routine. 

Glossary for Review 

binary search —the search method used whereby the list of possible 
items is reduced by one-half with each successive guess 

base 10 numbering system —the common numbering system that uses 
the ten digits 0 through 9 to represent all possible values 

hexadecimal numbering system —the computer-oriented numbering 
system that uses the 16 digits 0 through F to represent all possible values 


Quiz 


1. Why weren’t min_num and max_num (originally 1 and 100, respec¬ 
tively) declared as constants with the #define directive in the number 
guessing program? 

2. Why weren’t the three routines inch_to_meter, cub_inch_to_meter, and 
ounce_to_gram combined into one routine, since they all perform some 
sort of conversion? 

3. Why was “do . . . while” loop used in the tape counter program instead 
of a regular “while" loop? 

4. Are there any numbers that have the same representation in both hexa¬ 
decimal and base 10 notation? 


117 










In this chapter, you will learn: 

• More about data types. 

• The concept of arrays, and how they are used in programs. 

• About the struct and file types. 

• The concept of recursion, and how to use it in C. 






Learning C on the ST 


Advanced Numeric Types: Longs, 
Doubles, Words, and Unsigned Types 


We have already used the LONG, short, and WORD variable types, but it 
is worth noting some of the reasons behind using them versus just declaring 
an int type. We have stated that the maximum range for a long int is from 
-2,147,483,648 to 2,147,483,647 and should accommodate most of your 
needs. You will probably never need to work with a value outside this range, 
and, in fact, it would be more efficient if you could use a variable that did 
not take up the four bytes that a long int uses. To this end, C provides the 
WORD or short declaration types. WORD and short are interchangeable in 
Lattice C in that they use only two bytes of memory (WORD is declared as 
#define WORD short). On other compilers, such as Megamax, WORD is 
defined in the portab.h file as an int (two bytes), whereas a short int is 
defined to be one byte. The WORD or int type range is from -32,768 to 
32,767, which still provides a rather large range of values, but only requires 
half the memory of a long int. 

In addition to these fundamental integer types, a variable can be declared 
to be an unsigned short, int, LONG, or WORD. For example, an unsigned 
int would have a range of 0 to 65,535. The unsigned int may hold a larger 
number than the regular int, but it still does so in only two bytes. How is 
this possible? In an int, the two bytes are comprised of 16 bits (8 bits per 
byte), and one of the bits is used to determine if the number is positive or 
negative and is commonly referred to as the sign bit. The unsigned int does 
not need to use one of its bits as an indicator of the sign and can therefore 
result in producing a larger positive number. However, both types have iden¬ 
tically sized ranges, because there are only so many combinations of numbers 
that may be represented by 16 bits! 

The unsigned modifier may be used on any of the types we have used 
so far with the same results as described for ints. For real numbers, the float 
type is four bytes in length and a long float, or double, is eight bytes long 
and provides a more precise real value. 

You will notice that many of the GEM library routines use WORDs, 
shorts, etc. for parameters, and you should always be careful to match your 
variable sizes to those of the library routines. Quite often you will find a bug 
with a program that arises because of a size mismatch. In addition, if you 
are converting programs from, say, Lattice C to Megamax C, be aware that 


120 








Advanced Data Structures and Concepts 


Lattice treats ints and long ints identically, and some of the parameters in 
the GEM functions may need to have different types in the two versions. 


Simple Arrays 


Up to now you have been exposed to the basic types of C including int, float, 
char, etc. These types are quite useful indeed, but would be somewhat limited 
for general programming without a mechanism to further categorize somehow 
groups of similar variables. The array structure is one that greatly adds to 
the programmer’s set of tools for solving problems. We have already seen 
how arrays may be used to create strings like this: 

char my_string(10]; 


This declaration reserves ten bytes of memory for storing the characters that 
you wish to place in my ^string. It might be helpful to picture these ten bytes 
as being laid out as follows: 

U U U U U U U IJ u u 

0123456789 

my_string 

We could then assign a value to my_string via the gets function like this: 
gets(my_string); 


Let’s say that we enter the name Kelly when the gets requests our input; now 
my_string looks like this: 


l K l M 
o 1 


111 

2 


III lyl |\o| |_| |_| |_| L 

3 4 5 6 7 8 9 

my_ string 


As you can see, each of the characters in the name Kelly are stored one after 
another in my_string, and the 6th position is filled with a \0 or a null char¬ 
acter. This is because C has to have some way of knowing exactly how many 


121 




Learning C on the ST 


characters in the ten-byte my_string location are used to make up the string. 
In other words, C starts at the first location of my_string and continues to 
view each character as part of the string until a null character is encountered. 
Other languages have different ways of determining the length of strings. For 
example, many implementations of PASCAL use a byte or two before the 
string to hold the string length; this requires at least one extra byte per string 
and might therefore be viewed as excess memory usage. But remember that 
C needs one byte to hold the null termination character in its string repre¬ 
sentation. 

Even though we declared my_string to hold ten characters, we could 
very easily assign a string with, say, 15 characters to my_string. What do 
you think would happen? If you have any variables declared after my_string, 
the contents of my_string will overflow into the next variable(s). Again, 
your variables are stored sequentially in memory, and although we specified 
that we only needed ten bytes of memory in our declaration ot my_string, 
C looks at the declaration as an address at which to place the first character 
of the string; additional characters are placed one after another in memory 
until the end of the string. So, be careful when assigning strings, because it 
is very easy to trash another variable by overwriting it with a lengthy string. 

Now that you are more familiar with the layout and structure of simple 
arrays in the form of strings, let’s see how we can use the array concept to 
solve some programming problems. For instance, let’s say you were writing 
a program that displays the number of wins of six baseball teams. In order 
to have these figures placed in variables, we would have to make declarations 
like this: 

int teaml; 
int team2; 
int team3; 
int team4; 
int team5; 
int team6; 

We can assign the values to these variables like this: 

teaml = 50; 
team2 = 75; 
team3 = 37; 
team4 = 45; 
team5 = 57; 
team6 = 27; 











Advanced Data Structures and Concepts 


We could have created an array that would hold each of these values 
like this: 

int team_array[61; 

and we would make the assignments like this: 

team_array[01 = 50; 
team_array[1] = 75; 
team_array[2] = 37; 
team_array[3| = 45; 
team_array[41 = 57; 
team_array[5| = 27; 

We say that the variable team_array is “an array one to six of type 
int.” In other words, team_array may have up to six elements, each element 
is an int, and the array is indexed from 0 to 5. The last portion of that state¬ 
ment is worth noting: Although the index has a defined size of six, the ref¬ 
erencing index begins at zero and goes to one less than the defined size. 
Picture the team_array as a set of six slots whose values after assignment 
look like this: 

51_27_1 

41 57 1 

31 45 1 

21 37 1 

H 75 __ 1 

Of 50 1 

team_ array 

Array elements may be assigned to one another just like other variable 
types. So we could say: 

team_array[1] = 45; 
team_array[2] = team_arrayl1]; 

which would place the value 45 in both team_array[l] and team_array[2]. 
Arrays don’t have to contain int types; we could have an array of float, char, 
LONG, etc. 


123 






Learning C on the ST 


In general, the array’s syntax is: 

array-type var_name[number of elements); 

where number of elements represents the range (such as [10], meaning in¬ 
dexed from 0 to 9), and array-type is the predefined type of each element. 


Parallel Arrays 


One of the most popular applications of arrays utilizes the concept of parallel 
arrays, which are arrays that are related to one another by index. Consider 
the situation of a teacher with 30 students who would like to write a program 
that prompts for each student’s identification number (ID) and grade and then 
sorts them in descending order. The major data structures lor this problem 
might look like this: 

int id_array[301; 

int grade_array; 

If we were to assign the first three elements of each array based on this 
information: 


Student ID 

Grade 

1234 

84 

5678 

100 

3456 

91 


with this code: 

id_array[0] = 1234; 
grade_array[0] = 84; 
id_array[1] = 5678; 
grade_array[1] = 100; 
id_array[2] - 3456; 
grade-array[2] = 91; 

the arrays would look like this: 


124 









Advanced Data Structures and Concepts 


2[^3456__] 2 \ 91 1 

II 5678 1 H 100 1 

Q[ 1234 1 Or 84 1 

id_array grade_array 

As you may have noticed in the assignment statements above, each in¬ 
dividual student’s ID and grade are located in matching locations in the two 
arrays (i.e., ID 5678 is in slot 1 of the id_array, and that student’s grade 
is also in slot 1 of the grade_array). This may seen rather trivial on first 
inspection. However, when the items are later sorted in descending grade 
order, it is imperative that the relationship remain intact. Otherwise there 
would be no way of knowing which ID corresponds to which grade! 


Structures and Files 


The student ID/grade example discussed earlier might better be solved through 
the use of a record data structure. A structure or, record, is a collection of 
one or more different item(s) or field(s) that are grouped together for logic 
and convenience. For instance, this declaration would provide a structure for 
our student ID/grade problem: 

struct student-rec 

{ 

int id; 

int grade; 

}; 


In our student-rec type we say that we have two fields: id and grade. In 
general, the structure type declaration looks like this: 

struct rec_type 

{ 

typel fieldl; 
type2 field2; 


typen fieldn; 

}; 


125 






Learning C on the ST 


We must next declare a variable of this type so that we may work with 
it in a program: 

struct student_rec a_record; 

In order to make an assignment to a structure’s fields we must specify 
the structure’s name followed by a dot (.) and then the field name itself. Here 
is how we could assign values to a_record: 

a_record.id = 6789; 

a_record.grade = 91; 

It’s as simple as that! But if we assign new values to those fields (such as 
the next ID and grade), the original values are lost. A simple solution to that 
would be to declare an array of structures like this: 

struct student_rec 

{ 

int id; 

int grade; 

}; 

struct student_rec class_array[30l; 

We could now make our student ID/grade assignments like this: 

class_array(0].id = 1234; 

class_array[0]. grade = 84; 

class_array(1].id - 5678; 

class_array[11.grade = 100; 

class_array(2].id = 3456; 

class_array(2].grade = 91; 

Instead of creating an array of structures, we could have created a file 
that is simply a collection of records on a disk. The following is a valid file 
declaration: 

FILE *my_class; 


126 


This declaration says that my_class points to a file; the contents of the file 
are at this point unknown, but when the my_class value is used in file ma¬ 
nipulation routines, the structure format is passed as a function parameter. 





Advanced Data Structures and Concepts 


We can use the FILE type to store our various entries on a disk for 
later retrieval. To understand better the relationship between files, records, 
and fields, think of the filing cabinet in a doctor’s office. The cabinet is a 
file because it is full of folders on different patients. Each patient’s folder is 
a record, and each item in that folder (i.e., name, address, height, and weight) 
is a field. Now let’s look at a program that uses arrays and structures to create 
a baseball team lineup. 


Using Arrays and Structures in an Application 


The following program requests information for nine players on a baseball 
team, including name, position, batting average, and spot in the batting order. 
When this information is entered for each player, the array of players (struc¬ 
ture) is scanned through to display the batting order of the team. Let’s take 
a look at the program line by line: 


/"Baseball Lineup Program*/ 


#include (stdio.h) 
#inculde (gembind.h) 
#include (define.h) 

struct player_rec 

{ 

char name[201; 
char position[15j; 
int average; 
int bat_order; 

}; 


/"record for each player*/ 

/"player's name*/ 

/"player's position (e.g. shortstop)*/ 
/"player's batting average (e.g. 300)*/ 
/"player's spot in batting order*/ 


struct player_rec team_array[9]; /"array of players*/ 

int order_set[9|; /"batting order positions*/ 

int work_stn_hndle,handle; 

int contrl[12], intinfl28], ptsin[128], intout[128], ptsout|1281; 


main() 

{ 

int nothing; 

window_set_up(); 
graf_mouse(M„OFF,&nothing); 
get_player_info(); 
display_player_info(); 


127 








Learning C on the ST 


graf_mouse(M_ON,&nothing); 
clean_up(); 

} 

window_set_up( > 

{ 

short loop; 
int work_in[11]; 
int work_out[57j; 
int dummy; 

appl_init(); 

handle = graf_handle(&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle = handle; 

fordoop = 0; loop < 10; loop+ + )work_in(loopj = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up{) 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit(); 

} 

get_player_info{) 

{ 

int counter; /*dummy counter*/ 

int valid; /*is this entry valid?*/ 

for (counter = 0; counter <=8; counter+ + ) 
order_set[counter] = FALSE; /*initialize order_set to no 
entries*/ 

for (counter = 0; counter <=8; counter+ + ) 

{ 

printf(''What is this player's name?"); 
scanf("%s",&team_array[counter].name); 
printf("What is this player's position?"); 
gets(team_array[counter].position); 

printf("What is this player's batting average? (e.g. 300)"); 
scanf("%d'\&team_array[counter]. average); 
do 
{ 

valid = TRUE; 

printf("What is this player's spot in the batting order (e.g.2)?"); 


128 







Advanced Data Structures and Concepts 


scanf("%d”,&team_array[counter].bat_order); 
if(order_set[team_array [counter].bat_order]) 

printf('\nThat spot in the order has already been used!\n\n"); 
valid = FALSE; 

} 

else 

{ 

order_set[team_array[counter].bat_order] = TRUE; 
printf(( ,, \n"); 

}• 

} 

} 


display_player_info() 

{ 

int which_batter; /*dummy counter*/ 
int counter; /*dummy counter*/ 

printf("\n\nYour batting order is as follows:\n\n"}; 
for(which_batter = 1; which_batter <=9; which_batter+ + ) 

{ 

counter = 0; 

while (team_array[counter].bat_order!= which_batter> 

++counter; 

printfl" Batter %d is %s who is playing %s/n", which_batter, 
team_array[counterj.name,team_array[counter].position); 

} 

printf(''\n\nPRESS RETURN TO CONTINUE. . ."); 
getchar(); 

} 


When you run this baseball program, you are requested to enter player 
name, position, batting average, and position in the batting order for nine 
team members. If for any reason you should enter two players with the same 
batting order position, the program will note this discrepancy with an error 
message and request you to enter another batting order position for that player. 

The baseball program has two functions—one for gathering the player 
information (get_player_info()) and another for displaying the information 
(display _pIayer_info()). 

The main program’s declarations begin with a structure for the players 


129 









Learning C on the ST 


that contains fields for name, position, batting average, and position in the 
batting order. The team_array array is then declared so that it may hold nine 
player records, and an array called order_set is declared to hold nine ele¬ 
ments that indicate whether each of the nine batting order positions is already 
occupied. The main program merely performs the usual initialization and calls 
both get_player_info() and display_player_info(). 

The routine get_player_info() begins by initializing the order_set to 
all FALSE values. Then for each player the name, position, average, and 
batting order position are requested. During the request for batting order po¬ 
sition the order_set is used to make sure that no two players have the same 
position in the batting order (which would make no logical sense, but because 
the user could mistakenly enter the same number twice, we have incorporated 
this error handling mechanism). Again, if it is determined that the number 
the user entered for the batting order is the index of an entry that is already 
set to TRUE, an error is displayed; otherwise, that slot in the order_set is 
set to TRUE. 

The function display _player_info() simply scans through the team_array 
for each successive position in the batting order, and with each one that it 
finds, it performs a printf, which displays the player’s name and position. 

Enter the lineup of your favorite team, or make one up to see how the 
program works in various situations. Be sure to attempt to enter the same 
batting order position for two or more players. 

As you can see, advanced data structures like arrays and records have 
significant use within C and in fact are complementary to one another in 
assisting the programmer with smooth, readable code. 

Recursion (or, Can I Call Myself?) 


We have seen how functions can be used in C to simplify the task of writing 
lengthy programs by centralizing common operations and separating logical 
blocks of code. We have also explained that functions can be called or ref¬ 
erenced by the main routine or other routine depending on the program scope. 
One point we have not discussed, however, is that of a function calling itself. 
For example, let’s say we want to write a program that, given a particular 
integer, calculates the product of itself and every integer between itself and 
1 (i.e., given 4, the result is 4*3*2* 1 = 24). (This mathematical concept is 
known as ‘‘factorials” and is written as 4!, which is read “four factorial.”) 
This program accomplishes the job: 


130 







Advanced Data Structures and Concepts 


/•Recursion Program*/ 


#include (stdio.h) 

#include (gembind.h) 

#include (define.h) 

int work_stn_hndle, handle; 


int done; 
int valid; 
int fac_num; 
int temp_num; 
long float result; 
char again; 


/*are we finished yet?*/ 

/*is this a valid number?*/ 

/•number to factorialize*/ 

/*temp variable used to perform factorials*/ 
/•the result of factorializing again*/ 

/*Y/N answer to calculate again*/ 


int contrl[12], intin[128], ptsin[128], intout[128], ptsout[128]; 


main!} 

{ 

int nothing; 

window_set_up(); 
graf_mouse(M_OFF,&nothing); 
done = FALSE; 
while (!done) 

{ 

result = 1; /*initialize result*/ 
valid = FALSE; 
while (’valid) 

{ 

printf("Please enter an integer between 1 and 150 which you\n"); 
printC'want to factorialize. . ."); 
scanf("%d'',&fac_num); 
if {(fac_num > 0) && (fac_num <= 150)) 
valid = TRUE; 
else 

printf('\n\nlNVALID RESPONSE. . .PLEASE TRY AGAIN!\n\n\n"); 

} 

temp_num = fac_num; 
calc_fact(); 

printf("\n\n\nThe result of factorializing %d is %g.\n",fac_num,result); 
printf(''Would you like to calculate another number(Y/N)?"); 
again - getcharO; 
if ((again == 'N') || (again == 'n')) 
done = TRUE; 
else 

printf("\n\n"); 


131 





Learning C on the ST 


} 

graf_mouse(M_ON,&nothing); 
clean_up{); 

} 

window_set_up{) 

{ 

short loop; 
int work_in(11]; 
int work_out[57]; 
int dummy; 

appl_init(); 

handle = graf_handle(&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle = handle; 

for (loop = 0; loop < 10; loop+ + )work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit(); 

} 

calc_fact() 

{ 

while(temp_num != 1) 

{ 

result *= temp_num; 

—temp_num; 
calc_fact(); 

} 

} 

The program accepts a number between 1 and 150 and through the re¬ 
cursive routine calc_fact calculates the factorial of the given number. 
Calc_fact() is said to be recursive because it calls itself (at the end of the 
“while” loop). It makes sense to set up the program in this manner, since 
we are repeatedly performing the same operation on a number. However, the 
same goal can be accomplished with another “while” loop in calc_fact(). In 
fact, most (if not all) recursive functions can be eliminated by coding the 
routine in a different manner. Try to avoid programming recursively for three 
major reasons: 





Advanced Data Structures and Concepts 


1. it is very difficult for someone else (and sometimes even for your¬ 
self) to follow the logic of a recursive routine; 

2. recursive routines are famous for causing programs to “crash” be¬ 
cause they eat up a lot of memory; and 

3. it is very easy to get into an infinite loop with a recursive routine 
where the “while” loop used to control the recursive call never has 
its condition check set to FALSE. This could occur because the loop 
counter is being incremented instead of decremented, etc. 

At any rate, it is recommended that you exhaust every other program¬ 
ming method before embarking on recursive routines. 

Glossary for Review 

sign bit —the bit used in signed numbers that indicates whether the num¬ 
ber is positive or negative 

array —a collection of like items that are categorized by an index, which 
points to a location within the structure 

record —a data structure that contains one or more item(s) or field(s) 
and is used to centralize their meaning and use 

field —the smallest divisible portion of a record 

file —a collection of records 

recursion —the act of a function calling itself in a repeating fashion (one 
of the most popular examples is the mathematical factorial function) 


Quiz 


1. Describe the relationship between fields, records and files. 

2. Why does it not make sense to try and put as many fields as possible in 
a recored declaration? 


3. What was the purpose of the “for” loop in the baseball program, which 
successively set the items in the order_set to FALSE in the get_player_info 
function? 


133 










More on Structures and Files 


In this chapter, you will learn: 

• About the organization of fields within structures and structures 
within files. 

• The similarities between files and arrays of structures. 

• Some of the most useful file handling library routines. 

• How to sort and merge structures within a file. 


J 




Learning C on the ST 


Fields, Structures, and Files 


Every day we deal with files of many different types. Your local phone book 
is one of the most obvious examples of a file. It consists of numerous entries, 
such as your own, that contain names, addresses, and phone numbers of local 
residents. Below is a portion of a sample phone book: 


Craig, Joe D. 
Staton, Mike 
Mackowick, Paul 
Zimmerman, Terry 


451 Emerson Rd. 555-1200 
2009 Sidney wood Rd. 555-1201 
1487 Altaview Ave 555-1202 
1234 Miamisburg Rd. 555-1203 


The entire book is called a file, each entry is a structure, or record, and 
each structure has three fields: name, address, and number. The following C 
structure could be used to represent a phone book record: 


struct phone_type 

{ 

char name[25); 
char address(30]; 
char number[9l; 

}; 


If we then make the following variable declaration: 

struct phone_type ph_var; 

we could read in the fields of the structure like this: 

printfL'What is the person's name? 

gets<ph_var.name); 

printfC'What is their address? "); 

gets(ph_var.address); 

printf("What is their phone number? "); 

gets(ph_var.number); 

We would then want to put these in what we have been referring to as 
a file but we don’t know how to do this yet so let’s look at another alternative 
to storing this information in a familiar manner. 






More on Structures and Files 


Files vs. Arrays of Structures 


If we wanted to work with several phone book entries within a program we 
could declare a variable like this: 


struct phone_type ph_bk_array[10]; 


which would allow us to read in up to 10 entries like this: 


for (i = 0; i < 10; i + +) 

{ 

printf("What is the person's name? "); 
gets(ph_bk_array[i J.name); 
printfC'What is their address? "); 
gets(ph_bk_array[i J.address); 
printf("What is their phone number? "); 
gets(ph_bk_array[i ]. number); 

} 


We could then manipulate this array so that it is sorted alphabetically 
by last name, phone number, etc. The main problem arises when we want 
to save the information for future use; when you end the program, the array’s 
contents are forever lost, and although you could have declared a larger array, 
you will always be limited by the size of the array if you want to work with 
many more entries. 

The C File type could be used to simplify this problem. We would need 
to declare a file within the program such as this: 


FILE *phone_file; 

This declaration will later allow us to place multiple phone_type structures 
on a disk and in a manner that we can later retrieve and manipulate. The file 
is then the obvious choice for storing your phone book entries, since the 
information may be retrieved after the program has been terminated, and the 
number of entries is usually limited only by the available disk space. The 
manipulation of files involves four major C functions: fopen, fread, fwrite, 
and fclose. 


137 



Learning C on the ST 


File Handling Library Routines 


In order to access a file for reading or writing, you must first open it with 
this function: 

ptr_to_file = fopen(file_name,access-type); 

where ptr-to_file is the FILE variable used in the program to reference the 
file (e.g., phone_file as declared above), file_name is the name of the file 
as it appears on the disk (or as it will be named when viewed as an icon in 
the disk window), and access-type is the mode for which the file is to be 
opened. Possible values for access_type include “r” tor read mode, “w” for 
write mode, and “a” for append mode. 

Once you have opened a file, you may then either read or write to it 

using these functions: 

fread(&struct_var,struct_size,num_of_structs,ptr_to_file); 

fwrite(&struct-var,struct-size,num_of_structs,ptr_to-file); 

where, again, ptr_to_fi!e is the FILE variable used in the program to ref¬ 
erence the file, struct-var is the item (notice that the address is passed via 
the & operator) you wish to read from or write to the file. struct_size is the 
size of the struct-var, and num_of_structs is the number of structures to 
be read/written. Rather than figuring exactly how large the struct_var is, it 
is common to use the sizeof operator like this: 

fread(&struct_var,sizeof(struct_var),num_of_structs,ptr_to_file); 


or: 


fread(&struct_var,sizeof(struct 

struct_type},num_of_structs,ptr_to_file); 

Either one of these methods will provide the desired results, since the sizeof 
operator will determine the exact size of the struct-var, whether you use 
sizeof with the variable (struct-var) or its structure type (struct-type). Be 
careful, however; if you do use the structure type, make sure that you precede 
it with the reserved word struct. 





More on Structures and Files 


When you are finished with a file, you should always close it with this 
function: 

fclose(ptr_to_file); 

There should always be a corresponding fclose performed upon every 
fopen-ed file. If you do not follow that simple rule, you could end up with 
many problems in your programs. 

Finally, when reading from a file, it is an error to attempt to read beyond 
the last entry in the file or to try to read from a nonexistent file, since there 
is nothing there! You can ensure that you have successfully read in an entry 
(and therefore not be at the end of the file) by setting up your reading loop 
like this: 

while {fread(&phone_record,sizeof(struct phone_type), 
1,phone_file)==1); 

As long as the result of fread is equal to 1, we have been able to read another 
entry, and we aren't at the end of the file. 

In order to avoid the problem of reading from a nonexistent file, we can 
check the result placed in the file pointer against the NULL value (as #de- 
fined in the portab.h file in Megamax); if it equals NULL, the file does not 
exist on the disk. Here’s an example of how to check for this situation: 

if((phone_file = fopen(name,"r"))!= NULL) 

/*ifs safe to attempt an fread*/ 

Using a File in a Phone Book Program 

The concepts described above are brought out in this program, which may 
be used to keep your own private phone book on a disk: 

/*Phone Book Program*/ 

#include (stdio.h) 

#include (portab.h) 

#include (gembind.h) 

#include (define.h) 

struct phone_type 

{ 


139 









Learning C on the ST 


char first_name[10]; 
char last_name[10]; 
char number[9]; 

}; 


/•entry's first name*/ 
gentry's last name*/ 
/*entry's phone number*/ 


int work_stn_hndle,handle; 
int nothing; 

char user_file_name[10]; /*user's phone book file name*/ 

FILE *phone_file; /*phone book file pointer*/ 

char response; /*menu selection*/ 

int done; /*are we finished?*/ 

struct phone_type phone_record; /*variable for reading/writing to file*/ 

int contrl[12], intin[128], ptsin[128], intout[128], ptsout[128]; 

main() 

{ 

window_set_up{); 
graf_mouse(M_OFF,&nothing); 
done = FALSE; 

printf(''Enter your file's name: "); 
scanf("%s",&user_file_name); 
while (!done) 

{ 

printf("\n\n Which do you wish to do:\n\n”); 
printf(" 1. Add a record to the file\n">; 
printf{" 2. Search the file\n''); 
printf<" 3. QUIT\n\n"); 
printf{"PLEASE ENTER A 1, 2 OR 3 "); 
switch( response = getchar(» 

{ 

case T:add_record(); 
break; 

case ‘2':search_file(); 
break; 

case '3':done = TRUE; 
break; 

default:printf("\n\nlNVALID RESPONSE. . .PLEASE TRY 
AGAIN!\n"); 

} 

} 

graf_mouse(M_ON,&nothing); 
clean_up(); 

} 





More on Structures and Files 


window_set_up{) 

{ 

short loop; 
int work_in[11]; 
int work_out|57]; 
int dummy; 

appl_init(); 

handle = graf_handle{&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle = handle; 

for(loop = 0; loop < 10; loop+ + )work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit(}; 

} 

add_record{} 

{ 

printf("\n\nWhat is this person's first name? "); 
scanf("%s”,8iphone_record.fi rst.name); 
printfC'What is this person's last name? "); 
scanf("%s'',&phone_record.last_name); 

printf(''What is this person's phone number? (e.g., 555-1212) "); 
scanf("%s",8«phone_record.number); 

phone_file = fopen(user_file_name/'a"); /^append to the file*/ 
fwrite(8iphone_record,sizeof(struct phone_type),1,phone_file); 
fclose(phone_file); 

} 

search_file() 

{ 

char find_nameI10]; /*name use wants to search for*/ 
int found; /*have we found the name yet?*/ 

printf("\n\nWhat is the last name you wish to search for? "); 
scanf("%s",&find_name); 

if((phone_file = fopen(user_file_name/'r"))!= NULL) 

{ 

found = FALSE; 


141 










Learning C on the ST 


while {(Ifound) && (fread(&phone_record,sizeof(struct phone_type), 
1,phone_file) == D) 

if(strcmp(phone_record.last_name,find_name) == 0) 
found = TRUE; 
if (found) 

printf("\n\n%s %s's",phone_record.first_name, 
phone_record.last_name); 

printff" phone number is: %s\n'',phone_record.number); 

} 

else 

printf("\n\nYour file contains no number for %s\n",find_name); 
fclose(phone_file); 

} 

else 

printf("\n\nThere is no file called %s on the disk!\n",user_file_name); 

} 


This program may be used to create your own disk-based phone book 
file, add records to it, and search for particular entries. When you run the 
program, you will see that a menu is displayed that asks you to select either 
to add a record, search the file, or quit. If you choose to add a record, the 
program requests the first and last name and phone number of the person to 
add to the file. If you choose to search the file, you are requested to enter 
the last name of the person you are searching; if the name is on the file, the 
full name and number will be displayed. After every add and search, the 
original menu is redisplayed so that you can perform several additions/searches 
without quitting the program. 

The FILE, which is declared in the program, is called phone_file and 
is used to write/read the phone_record structure, which has fields for first 
and last names and phone number. Notice how the file pointer declaration 
(FILE *phone_file;) makes no reference whatsoever to the type of structure 
that is being written to that file (phone_type). 

Aside from our usual initialization/cleanup routines, the program is bro¬ 
ken up into the main routine and two functions, add_record() and 
search-file(), which place an entry at the end of the file and find a particular 
entry within the file, respectively. 

The main program performs the initialization and asks the user for a file 
name (user_fiie_name) to use in the remainder of the program. The program 
menu is then displayed, which includes the options to add a record, search 
the file, and quit the program. Depending on the user's selection, either 


142 








More on Structures and Files 


add_record() or search_file( ) will be invoked, or the variable done will be 
set to TRUE and the program will be terminated. 

When add_record() is invoked, the function requests the first name, 
last name, and phone number of the entry to be added to the file. The file 
is then fopen-ed, and the entry is appended to the end of the file, and the 
file is then closed via fclose. (Recall that the a option in fopen provides us 
with the ability to append to the file.) 

When the search_file( ) routine is called, the user is asked which last 
name he/she wishes to locate in the file. The file is then fopen-ed, and the 
search is begun through a “while" loop, which continues until either the last 
name is found or the end of file is reached (again, we can check the end of 
file by comparing the result of the fread to 1, which would be TRUE if we 
were able to read in one record). If the name is found, the entire name is 
displayed along with its associated phone number; otherwise, a message is 
displayed that explains that the name is not in the file. In either situation, 
the file is then closed before returning to the main routine. 

The phone book program creates a very simple file; there is no real order 
to the entries within the file. When a new entry is added to the file, it is 
placed at the end and not in alphabetical order like your local phone book. 
This should not create a problem for a small file that is infrequently used, 
but if there are quite a few entries in your file and you are searching it often, 
it might be nice not to have to look at almost every entry in the file before 
finding the one you want. If we add more logic to the program so that the 
file is stored in alphabetical order, you would notice a difference in speed 
when accessing a particular entry. To do this, we need to discuss the concepts 
of sorting and merging entries. 


Sorting and Merging Entries 


Your local phone book is a compilation of names and numbers in alphabetical 
order. If the book were not in any order, it would take hours to find the 
number of a friend. Our phone book program would have just that problem, 
but since the computer can search files so quickly, this lengthy period of time 
goes almost unnoticed, unless the file to be searched is quite large. In order 
to make our phone book program create a file similar to your local phone 
book, we must have the program sort the entries in the file before actually 
writing to and closing it. If we need to add an entry that would be the last 
one in the file, we simply add it to the end like we did in the original program. 


143 






Learning C on the ST 


But when we need to insert the entry at the beginning or between two existing 
entries, we must make room for the new entry and merge it with the existing 
ones. The following program has the modifications necessary to create an 
alphabetical phone book file: 

/•Sort/Merge Phone Book Program*/ 

#include (stdio.h) 

#include (portab.h) 

#include (gembind.h) 

#include (define.h) 

struct phone_type 

{ 

char first_namel10]; 
char last_name[10]; 
char number[9j; 

}; 

int work_stn_hndle,handle; 
int nothing; 

char user_file_name[10]; 

FILE *phone_file; 

struct phone_type phone_array[50]; /*array for sorting*/ 
struct phone_type phone_record; 
char response; 
int done; 

int index; /*phone_array index*/ 

int counter; 

int contrl[12], intin[128|,ptsin[128],intout|128],ptsout[128]; 

main() 

{ 

window_set_up(}; 
graf_mouse(M_OFF,&nothing); 
done = FALSE; 

printf("Enter your file's name: ">; 
scanf("%s",8tuser_file_name); 
while (Idone) 

{ 

printf("\n\nWhich do you wish to do:\n\n”>; 
printf(" 1. Add a record to the file\n''); 
printf(" 2. Search the file\n”); 
printf{" 3.QUIT\n\n"); 




More on Structures and Files 


printf("PLEASE ENTER A 1, 2 OR 3. . 
switch (response = getcharO) 

{ 

case T:add_record( ); 
break; 

case '2':search_file(); 
break; 

case '3':done = TRUE; 
break; 

default: printf("\n\nlNVAUD RESPONSE ... TRY AGAIN!\n"}; 

} 

\ 

graf_mouse(M_ON,&nothing); 
clean_up(); 

} 

window_set_up{) 

{ 

short loop; 
int work_in(11); 
int work_out[571; 
int dummy; 

appl_init(); 

handle = graf_handle(&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle = handle; 

for(loop = 0; loop < 10; loop+ + ) work_in[loop] = 1; 
workJn[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit(); 

} 

insert_and_sort() 

{ 

int located_slot; /*have we found the spot to insert record?*/ 
int dummy; /*used to slide array elements down in sort*/ 

located_slot = FALSE; 
counter = 0; 

while ({counter < index)&&{!located_slot)> 


145 









Learning C on the ST 


{ 

if (strcmp(phone_array[counter].last_name, 
phone_record.Iast_name} < 0) 

++counter; 

else 

located_slot = TRUE; 

} 

for {dummy = index; dummy > counter; dummy—) 
phone_array[dummy] = phone_array[dummy - 1); 
phone_array[counter] = phone_record; 

} 

add_record() 

{ 

int dummy; /*used as index for writing to file*/ 
index = 0; 

if((phone_file = fopen(user_file_name,"r'')) != NULL) 

{ 

while(fread(&phone_array[index|,sizeof(struct phone_type), 
1,phone_file) == 1) 

+ +index; 
fclose(phone_file); 

} 

printf("\nWhat is this person's first name? "); 
scanf("%s'',&phone_record.first_name); 
printf("What is this person's last name? "); 
scanf("%s'',&phone_record.last_name); 

printf{"What is this person's phone number? (e.g. 555-1212) "); 
scanf("%s'\&phone_record. number); 
insert_and_sort(); 

phone_file = fopen(user_file_name/ / w''); 
for (dummy = 0; dummy <= index; dummy+ + ) 
fwrite(&phone_array[dummy],sizeof(struct phone_type),1,phone_file); 
fclose(phone-file); 

} 

search_file() 

{ 

char find-name[10]; 
int found; 

int beyond; /*have we gone alphabetically beyond the name?*/ 
beyond = FALSE; 

printf(''\n\nWhat is the last name you wish to search for? "); 




More on Structures and Files 


scanf(''%s",&find_name); 

if {(phone_file = fopen(user_file_name,"r"}) != NULL) 

{ 

found = FALSE; 

while ((!found)&&{fread(&phone_record,sizeof(struct phone_type), 

1,phone_file) == 1) && (Ibeyond)) 
if (strcmp(phone_record.last_name,find_name) == 0) 
found = TRUE; 

else if (strcmp(phone_record.last_name,find_name) > 0) 
beyond = TRUE; /*we've gone too far in the file*/ 
if(found) 

{ 

printf("\n\n%s %s's", 

phone_record.first_name,phone_record.last_name); 
printfl" phone number is: %s\n",phone_record.number); 

} 

else 

printf("\n\nYour file contains no number for %s\n",find_name); 
fclose(phone-file); 

} 

else 

printf("\n\nThere is no file called %s on the disk!\n”user_file_name); 

} 


This new program uses a phone_array, which can hold up to 50 
phone-type entries and is used to sort the file before writing it to disk. The 
function add_record() has been changed to read the file into the array and 
then call the function insert_and_sort(), which finds the position within the 
array where the insertion should be made, slides all entries below this point 
down one position within the array, and then places the new entry in its 
proper position. Finally, the function search_file() no longer simply searches 
the file until it either finds the correct entry or hits the end of the file; it now 
knows that because the file is arranged alphabetically, once it gets past the 
point where the entry should be it quits. For example, if you are searching 
for the name Boyle and you are currently at the name Carlson in the file, 
you know that you have gone too far, and the name is not in the file. 

You probably will not notice much difference in this version of the pro¬ 
gram, but to see how the file itself is completely different, use the following 
program to display the contents of your phone book file. Be sure to use same 
file name that you used in the original phone book program. 


147 







Learning C on the ST 


/•Display Phone Book Program*/ 

#include (stdio.h) 

#include (portab.h) 

#include (gembind.h) 

#include (define.h) 

struct phone_type 

{ 

char first_name[10]; 
char last_name[10]; 
char number[9]; 

}; 

int work_stn_hndle,handle; 
int nothing; 

char user_file_name[10]; 

FILE *phone_file; 

struct phone_type phone_record; 

int control2], intin(128], ptsin[128], intout[128), ptsout[128]; 

main!) 

{ 

window_set_up(); 
graf_mouse(M_OFF,&nothing); 
printfC'Enter your file's name: "); 
scanf("%s",&user_file_name); 
printf("\n\n''); 

if((phone_file = fopen(user_file_name,"r")) != NULL) 

while (fread(&phone-record,sizeof(struct phone_type),1,phone_file) 
== 1 ) 

printf(“%s %s %s\n'' f phone_record.first_name, 
phone_record.last_name,phone_record.number); 
fclose(phone_file); 

} 

else 

printf("There is no file called %s on he disk!\n'',user_file_name); 
printf("\n\n PRESS RETURN TO CONTINUE. . 
getchar( ); 

graf_mouse(M_ON,&nothing); 
clean_up{); 

} 


window_set_up() 






More on Structures and Files 


{ 

short loop; 
int work_in[11]; 
int work_out[57]; 
int dummy; 

appl_init(); 

handle = graf_handle(&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle = handle; 

for (loop = 0; loop < 10; loop+ + ) work_in[loop] = 1; 
work_in(10] - 2; 

v_opnvwk(work_in ; &work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit(); 

} 

Throughout our discussion of structures we have looked at variables that 
are declared to be of the structure type. We can also declare a variable to be 
a pointer to a structure type like this: 

struct phone_type 

{ 

char first_name!10]; 
char Iast_namel10]; 
char number[9l; 

}; 


struct phone_type *an_entry; 

We can assign the address of our phone_record variable to an_entry like 
this: 


an_entry = &phone_record; 

Then we could look at what is in phone_record by using the —> operator 
(which works just like the dot (.) operator for nonpointer type structures) like 
this: 


if (strcmp(an_entry->last_name,find_name) -- 0} 


149 






Learning C on the ST 


which would be the same as saying this: 

if (strcmp(phone_record.last-name,find_name) -= 0) 

One of the most important reasons for working with pointers to struc¬ 
tures would be to pass them as parameters to functions, but for most appli¬ 
cations, you can very easily achieve your goals by using nonpointer type 
structure variables. 

Glossary for Review 

NULL —the value that may be compared to a file pointer variable to 
determine if the file is on disk; if the pointer equals NULL, the file does 
not exist (found in the portab.h header file listed in the Megamax manual 
but not on the disk—so, you must create the file yourself) 

sorting —the act of organizing entries in a particular order (i.e., alpha¬ 
betically, numerically, etc.) 

merging —the act of combining one set of entries with existing entries 
in a file so that the entire file is properly sorted 

Quiz -1 


1. How would you analogize a file to a collection of musical albums? 

2. In the phone book example, how were we able automatically to place all 
of our additions to the file at the end of the file? 

3. How would you modify all the phone book programs so that you don’t 
have to put the name of your phone book file in the programs each time 
you run them? 


150 





Debugging and File Analysis 


y 


In this chapter, you will learn: 

• Some tips on debugging programs with Megamax C. 

• A file dump utility that will allow you to view entire contents of 
files. 

• A file encipher program that will protect your files from unwanted 
viewing. 


J 







Learning C on the ST 


Debugging 


The program logic we have dealt with up to now has, we hope, been very 
easy for you to follow. Programs that are only a few dozen lines long can 
be written and debugged in a fairly short period of time. For those of you 
who are unfamiliar with the phrase debug, it is a term used in the computer 
world to represent the act of fixing program problems. A debugger is a utility 
that assists the programmer in fixing the problems by allowing him or her to 
step through the program line by line and look at or even modify variable 
values. So if you have run into a problem with a program, with the help of 
a debugger you might be able to look at a variable in question and see why 
it’s causing you headaches. 

If you are using the Lattice or Megamax C packages on your Atari ST, 
you probably already know that there currently is no debugging package 
available to help get your programs up and running. As the Lattice compiler 
documentation states, “A special debugger is required . . . which is not sup¬ 
plied with the package.” Although this is an unfortunate situation, you are 
certainly able to debug large programs without the use of a sophisticated 
debugger; we prefer to say that it’s just that much more challenging! And 
actually, without having a debugger handy, you are forced to think through 
the logic of your programs more than you might have to if you could rely 
on a debugger to look at the program while it’s running. Even if you are 
using a C compiler with a debug package, the following few debugging tips 
will probably come in handy some day, and you won’t have to work with a 
debugger to solve some problems. 

First of all, you should closely monitor the activity of your program for 
such things as disk activity and screen display. For instance, you might notice 
that one string you’re trying to display is making it to the screen, but the 
very next one is not. Or maybe you're working with a file-based program 
that is supposed to read/write a file on one of your disks. If you notice that 
the disk light never comes on before the program bombs out, you could look 
at the code before your disk command statements. As you can see with just 
these small bits of knowledge, you can better pinpoint the location of your 
problem. 

It is also worth noting that if you accidentally say something like this: 

if (a = b) 
instead of this: 

if (a == b) 


152 




Debugging and File Analysis 


you can wind up with some fascinating and head-scratching experiences. Some 
compilers may evaluate the first expression as TRUE, whereas others may 
view it as FALSE. At any rate, it is incorrect, and unfortunately some com¬ 
pilers won't even flag it as an error! So, be careful of forgetting the double 
equal signs when you’re working with “if” statements. 

If you’re having problems with a particular variable and you're not sure 
what value it is holding, it might be a good idea to use a few printf statements 
to display the variable at strategic locations with your code. This is commonly 
referred to as echo-printing because you are echoing the value of the variable 
to the screen. Although this process requires an extra compilation/link or 
two, in the end it could save you hours of distress. In fact, the author of this 
book relied heavily on echo-printing to solve several strange problems! If you 
do use echo printing, you'll quickly discover that you should look at strings 
in hex rather than their string format; if for some reason the string has a null 
value and you try to display it, it may appear that the echo-print did not work, 
whereas if you were to look at the string in hex on a byte-by-byte basis, you 
could see that the first byte was null. 

If you start working with very lengthy programs, you might find it con¬ 
venient to have a debug routine that you could call at any time to look at 
particular variables. This is a rather awkward way to try and fix problems, 
but once you get the function written, you would only be bothered with in¬ 
serting calls to it where you need them; you wouldn’t have to worry about 
duplicating large blocks of code and remembering to remove them once the 
problems are fixed. 

If you are working with a file-building program and you were able to 
achieve some disk activity, you could always look at what was written with 
the SHOW or PRINT options that are displayed when you try to open the 
files on the Atari desktop. Sometimes this is all you need to see exactly how 
much information got written to the file, but most likely you will discover 
that these options don't really show all the bytes that are in the file. For this 
reason, we have developed a file dump program that allows you to look at 
most of your files so that you can see exactly what they contain. 

File Analysis -1 


Let’s take a look at this program: 

/*Hex/Character file dump program*/ 

#include (stdio.h) 

#include (gembind.h) 


153 








Learning C on the ST 


#define NEW_LINE '\n' 
int work_stn_hndle,handle; 

int contrl[12], intin[128], ptsin[128], intoutll28], ptsout[128]; 

main(); 

{ 

int nothing; 

int i,j; /‘counters*/ 

char file_name[15]; /‘file to be dumped*/ 

FILE *file_ptr; /*pointer to the file*/ 

char a_char; /Character in file*/ 

char non_hex[16]; /*the array of printable characters*/ 

window_set_up(); 
graf_mouse(M_OFF,&nothing); 
printf(''Please enter the file name: "); 
scanf("%s",&file_name); 
printf("\n"); 

if ((file_ptr = fopen(file_name,"r")) != NULL) 

{ 

for (j = 0; j < 16; j+ + ) 
non_hex[j] = 7; /‘initialize the array*/ 
i = 0; 

while (((a_char = getc(file_ptr>) != EOF)) 

{ 

if (*a_char < 16) 
printf{" 0%x'',a_char); 
else 

printfi" %x",a_char); 

if ((*a_char >= ") && (*a_char <= ' ' '}) /‘printable*/ 
non_hex(i] = a_char; 

if (i == 7) /‘blank after every 8 characters*/ 
printf(""); 
else if (i == 15) 

{ /‘force out the non_hex array*/ 
printf(" ”); 

if <non_hex[15] == NEWSLINE) 
non_hex[15] = 
for (j = 0; j <= 15; + +j) 

{ 

printf("%c",non_hex[j]); 

non_hex[j] = /*re-initialize array*/ 

} 

printf("\n"); 


154 






Debugging and File Analysis 


i = -1; /*reset the counter; + + i will then set it to 0*/ 

} 

+ + i; 

} 

if (i >= 0) /^gracefully force out the rest of the last line*/ 

{ 

for (j = i; j <= 15; ++j) 

printf(" "); /*three blanks per hex dump space*/ 
if (i < 7) /*put in extra space for break between 8 and 9*/ 
printf(" "); 

printfi" "); /*space between hex and char*/ 
for (j = 0; j <= i - 1; ++j) 
printf("%c",non_hex[j]); 

} 

} 

else 

printf("NON-EXISTENT FILE ERR0R!\n”); 
printf("\n\n\n PRESS RETURN TO QUIT . . .”); 
getchar(); 

graf_mouse(M_ON,&nothing); 
clean_up(}; 

} 

window_set_up() 

{ 

short loop; 
int work_in[11]; 
int work_out[57]; 
int dummy; 

appl_int<); 

handle = graf_handle<&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle = handle; 

for (loop - 0; loop < 10; loop+ + ) work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

\ 


clean_up() 

{ 

v_clsvwk(work_stn_hndle}; 
appl_exit(); 

} 


155 








Learning C on the ST 


The hex/character dump program is fairly straightforward and intro¬ 
duces only one new C function: getc( ). As you probably assumed, this func¬ 
tion is used to get a character from the file specified as the parameter. We 
continue to call getc() until the EOF value is returned. 

When you run the program, you are prompted to enter the name of the 
file you wish to examine. Then, the file is displayed in both ASCII and the 
character translation. For example, if you enter the following file: 


Hi there. 

This is what a file dump looks like. 

As you can see, even numbers look kind of funny! 
1234567890 

But, this is a very good debugging tool for files. 


its file dump looks like this: 


48 69 20 74 68 65 72 65 
73 20 77 68 61 74 20 61 
6D 70 20 6C 6F 6F 6B 73 

73 20 79 6F 75 20 63 61 
76 65 6E 20 6E 75 6D 62 
20 6B 69 6E 64 20 6F 66 
31 32 33 34 35 36 37 38 

74 68 69 73 20 69 73 20 
6F 6F 64 20 64 65 62 75 
6F 6C 20 66 6F 72 20 66 


2E 

0A 

54 

68 

69 

73 

20 

69 

Hi there. . .This i 

20 

66 

69 

6C 

65 

20 

64 

75 

s what a file du 

20 

6C 

69 

6B 

65 

2E 

0A 

, 41 

mp looks like. . .A 

6E 

20 

73 

65 

65 

2C 

20 

65 

s you can see, e 

65 

72 

73 

20 

6C 

6F 

6F 

6B 

ven numbers look 

20 

66 

75 

6E 

6E 

79 

21 

0A 

kind of funny!. 

39 

30 

OA 

42 

75 

74 

2C 

20 

1234567890. But, 

61 

20 

76 

65 

72 

79 

20 

67 

this is a very g 

67 

67 

69 

6E 

67 

20 

74 

6F 

ood debugging to 

69 

6C 

65 

73 

2E 

0A 



ol for files. . . 


Each line of the dump represents 16 bytes of the file first in ASCII and 
second in the converted character representation. As mentioned earlier, ASCII 
is simply a common method of representing characters in a form that is rec¬ 
ognizable by computers. The hexadecimal ASCII conversion values for print¬ 
able characters are as follows: 


Hex Value 

Character 

Hex Value 

Character 

20 

(space) 

4F 

O 

21 

j 

50 

P 

22 

11 

51 

Q 

23 

# 

52 

R 

24 

$ 

53 

S 


156 



Debugging and File Analysis 


Hex Value 

25 

26 

27 

28 

29 
2A 
2B 
2C 
2D 
2E 
2F 

30 

31 

32 

33 

34 

35 

36 

37 

38 

39 
3A 
3B 
3C 
3D 
3E 
3F 

40 

41 

42 

43 

44 

45 


Character 

1/2 

& 

( 

) 

* 

+ 


/ 

0 

1 

2 

3 

4 

5 

6 

7 

8 
9 


< 

> 

? 

@ 

A 

B 

C 

D 

E 


Hex Value 

54 

55 

56 

57 
58 

59 
5A 
5B 
5C 
5D 
5E 
5F 

60 
61 
62 

63 

64 

65 

66 

67 

68 

69 
6A 
6B 
6C 
6D 
6E 
6F 

70 

71 

72 

73 

74 


Character 

T 

U 

V 

w 

X 

Y 
Z 
[ 

/ 

J 


a 

b 

c 

d 

e 

f 

g 

h 

i 

j 

k 

1 

m 

n 

o 

P 

q 

r 

s 

t 


157 




Learning C on the ST 


Hex Value 

Character 

Hex Value 

Character 

46 

F 

75 

u 

47 

G 

76 

V 

48 

H 

77 

w 

49 

I 

78 

X 

4A 

J 

79 

y 

4B 

K 

7A 

z 

4C 

L 

7B 

{ 

4D 

M 

7C 

1 

4E 

N 

ID 

} 


When you analyze your files with this program you will be able to see 
exactly how much information got written to the file on a byte-by-byte basis 
until the end of the file is reached. You may have noticed that there are 
several dots (V) in the character translation that actually weren’t in the orig¬ 
inal file. The dots represent nonprintable characters or those that are not easily 
represented on the screen. These are bytes that you would not be able to see 
with the SHOW option on the Atari desktop, since that option shows you 
only the printable characters. 

As you can see, this dump utility is quite useful in many applications. 
But now let’s look at a program that will allow you to encipher your data 
files so that nobody else can see what they contain. 

Try the following cipher/decipher program: 

/*encipher/decipher program*/ 

#include (stdio.h) 

#include <portab.h) 

#include (gembind.h) 

#define DISK_CIPHER "CIPHTEXT" 

int work_stn_hndle,handle; 

int nothing; 

char selection; 

short valid; 

char file_name[8]; 

FILE *text_file; 
char a_char; 

FILE *cipher_file; 


158 





Debugging and File Analysis 


main() 

{ 

window_set_up(); 
graf_mouse(M_OFF,&nothing); 
valid = FALSE; 
while (Ivalid) 

{ 

printf(" Please select the option you would like to do:\n\n"); 
printf(" 1. Encipher a text file\n\n"); 
printf(" 2. Decipher the enciphered file\n\n"); 
switch (selection = getcharO) 

{ 

case '1 'ivalid = TRUE; 
encipher(); 
break; 

case '2':valid = TRUE; 
decipher); 
break; 

default: printf("INVALID RESPONSE. . .PLEASE TRY 
AGAIN !\n\n"); 

} 

} 

printf("PRESS RETURN TO CONTINUE. . ."); 
getcharO; 

graf_mouse(M_ON,&nothing); 

clean_up(); 

} 

window_set_up() 

{ 

short loop; 
int work_in[11]; 
int work_out[57]; 
int dummy; 

apl_init(); 

handle = graf_handle(&dummy,&durnmy,&dummy,&dummy); 
work_stn_hndle - handle; 

for (loop = 0; loop <10; loop+ + ) work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up( > 


159 






Learning C on the ST 


{ 

v_clsvwk(work_stn_hndle); 
appl_exit(); 

} 

encipher( > 

{ 

printf("\nWhat is the name of the text file? 

scanf("%s",&file_name); 

if <(text_file = fopen(file_name,' y r'')) != NULL) 

{ 

cipher_file = fopen(DISK_CIPHER,"w"); 
while (((a_char = getc(text-file)) != EOF)) 
putc((a_char + 1),cipher_file); /*add one to encipher*/ 
fclose(cipher_file); 
fclose(text_file); 

printf{"\n\nYOUR FILE HAS BEEN ENCIPHERED!\n\n”); 

} 

else 

printf("\nNO SUCH FILE ON DISK!\n\n"); 

} 

decipherO 

{ 

if ((cipher_file = fopen(DISK_CIPHER,"r")) != NULL) 

{ 

while «(a_char = getc(cipher_file)) != EOF)) 
putchar(a_char - 1); /^subtract one to display*/ 
fclose(cipher_file); 
printf("\n"); 

} 

else 

printf("\nNO ENCIPHERED FILE ON DISK!\n\n"); 

} 


The basic premise behind the encipher program is to add the value 1 to 
every byte in the file and write the results out to a file called CIPHTEXT. 
In other words, all spaces would appear as a $21, a capital A would appear 
as a $42, etc. Now, the CIPHTEXT file will hold your enciphered file, and 
no one will be able to read it, unless of course they know the algorithm behind 
the encipher program. Then, when you need to view your file, you can run 
the program again with the decipher option, and it will display the deciphered 
file on the screen. The program can be easily modified to allow you to delete 




Debugging and File Analysis 


your original file, specify a user-defined name for the enciphered file, or 
decipher the enciphered file and write it to a file. 

Glossary for Review 

debugger —a utility that may allow you to view your program's exe¬ 
cution line by line and look at and modify variables 

echo printing —a debugging method of showing variable values at stra¬ 
tegic locations within your code 


Quiz 


1. Why is it sometimes beneficial not to have access to a sophisticated de¬ 
bugger? 

2. What does the following ASCII string say: 

43 20 49 53 20 46 55 4E ? 


161 








In this chapter, you will learn: 

• How to work with some of the more popular graphics routines 
available to you in the GEM library. 

• How to design programs that use a mouse-oriented routine, al¬ 
lowing you to utilize this important feature of the Atari ST. 










Learning C on the ST 


Atari ST Graphics in C 


As you know, your Atari ST is well-known for its graphics abilities. In fact, 
the level of graphics attainable on the ST is equivalent to the capabilities of 
systems costing four to five times as much. One of the reasons tor the ST's 
excellent graphics capabilities is the high number of pixels, or dots, per square 
inch on the high-resolution monochrome monitor screen; the more dots you 
have compressed in a given area, the crisper the resulting picture. 

In C, you also have access to the GEM graphics library, which is merely 
a compilation of useful graphics routines. For various reasons these routines 
are much faster and probably easier to use than most graphics routines you 
could develop yourself. 

To understand better the concepts behind graphics in C, try out this 
program, and before linking it, be sure to use a link control file (if you are 
using Lattice C only), which links in the gemlib.bin library; just remove the 
asterisk before the reference to this library in the standard control file. If you 
are using Megamax C, you need not do anything differently, since the linker 
in the shell automatically pulls in the necessary libraries. 

/•Graphics Example Program*/ 

#include (stdio.h) 

#include (gembind.h) 

#include (obdefs.h) 

#include (define.h) 

int work_stn_hndle,handle; 

int cntr[[12h intin[128], pts»n[1281, intout[128], ptsout[128); 
main() % 

{ *’ ’• I 

int nothing; 

window_set_up(); 
graf_mouse(M_OFF,&nothing); 

vsl_color(work_stn_hndle, BLACK); /*set drawing color to black*/ 
vsf_interior(work_stn_hndle,HOLLOW); /*set fill to hollow*/ 
draw_x(); 
draw_circle(); 
draw_empty_rec(); 
draw_filled_rec( ); 
draw_filled_rec(); 



164 


Graphics 


vst_color{work_stn_hndle,BLACK); /*set writing color to black*/ 
v_gtext(work_stn_hndle,150,350, 

"PRESS RETURN TO SEE TEXT VARIATIONS. . 
getchar(); 

v_clrwk(work_stn_hndle); /*clear the screen*/ 
do_text( >; 

v_gtext(work_stn_hndle,150,350, 

"PRESS RETURN TO QUIT THE PROGRAM. . "); 
getchar{); 

graf_mouse(M^ON,&nothing); 
clean_up(); 

} 

window_set_up( > 

{ 

short loop; 
int work_in(11]; 
int work_out[57]; 
int dummy; 

appl_init<); 

handle = graf_handle(&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle = handle; 

for(loop = 0; loop < 10; loop + + > work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit(); 

} 

draw_x() 

{ 

long num_of_points; /*number of points*/ 

WORD point_set[4]; /*actual line end points*/ 

num_of_points = 2; 

vsl_color(work_stn_hndle,BLACK); /*set drawing color to black*/ 

point_set[0] = 50; /*{50,50)*/ 

point_set[1] = 50; 

point_set[2] = 125; /*(125,150)*/ 

point_set[3) = 150; 


165 




Learning C on the ST 


v_pline(work_stn_hndle,num_of_points,point_set); /*draw line*/ 

point_set[0] = 125; /*{125,50)*/ 

point_set[1] = 50; 

point_set[2) = 50; /*(50,150)*/ 

point_set(3] = 150; 

v_pline(work_stn_hndle,num_of_points,point_set); /*draw line*/ 

} 

draw_circle() 

{ 

int x_coord; /*x coordinate of circle's center*/ 
int y^coord; /*y coordinate of circle's center*/ 

int radius; /*circle's radius*/ 

x.coord = 350; /*(350,100)*/ 
y_coord = 100; 
radius = 50; 

v_circle(work_stn_hndle,x_coord,y_coord,radius); /*draw the circle*/ 

} 

draw_empty_rect() 

{ 

int spec_array[4]; /*coordinate array*/ 

spec_array[0| = 50;/*array with (50,175), (250,325) opposite corners*/ 

spec_array[1] = 175; 

spec_array[2j = 250; 

spec_array[3] = 325; 

v_bar(work_stn_hndle,spec_array); 

} 

draw_.fi lled_rec() 

{ 

int spec_array[4] 

vsf_interior(work_stn_hndle,PATTERN); /*set the fill pattern to grid*/ 
vsf_style(work_stn_hndle,GRID); 

spec_array[0] = 300; /*array with (300,175), (500,325) opposite cor¬ 
ners*/ 

spec_array[1] = 175; 
spec_array[2] = 500; 
spec_array[3] = 325; 
vr_recfl(work_stn_hndle,spec_array); 


166 





Graphics 


} 

do_text{) 

{ 

vst_effects(work_stn_hndle,THiCKENED); 
v_gtext(work_stn_hndle,50,50/This is thickened text"); 
vst_effects{work_stn_hndle,SHADED); 
v_gtext(work_stn_hndle,50,100,"This is shaded text"); 
vst_effects{work_stn_hndle,SKEWED); 
v_gtext(work_stn_hndle,50,150,"This is skewed text"); 
vst_effects(work_stn_hndle,UNDERLINED); 
v_gtext<work_stn_hndle,50,200,"This is underlined text"); 
vst_effects(work_stn_hndle,OUTLINE); 
v_gtext{work_stn_hndle,50,250,"This is outlined text"); 
vst_effects(work_stn_hndle,SHADOW); 
v_gtext(work_stn_hndle,50,300,"This is shadowed text"); 
vst_effects(work_stn_hndle,0); /*return to no effects*/ 

} 

If you run this program, you will see that it displays a screen with a 
large X y a circle, an empty rectangle, and a filled rectangle. When RETURN 
is pressed after this screen is displayed, another screen is displayed that shows 
various text formats. 

Besides the usual initialization/cleanup routines, the program has five 
functions dedicated to drawing graphics and placing graphics text on the screen. 
First off, the main routine calls the routine vsl_color to set the line color to 
black; the format of this function call is: 

vsl_color(handle,color); 

where handle is the workstation handle, and color is any of the following 
values: 


Color Value 

WHITE 0 

BLACK 1 

RED 2 

GREEN 3 

BLUE 4 

CYAN 5 


167 




Learning C on the ST 


YELLOW 

6 

MAGENTA 

7 

LWHITE 

8 

LBLACK 

9 

LRED 

10 

LGREEN 

11 

LBLUE 

12 

LCYAN 

13 

LYELLOW 

14 

LMAGENTA 

15 


These colors are defined with the above values in the obdefs.h file. 

Next, the interior fill style is set to hollow via the vsf_interior function, 
which has the following format: 

vsf_interior(handle,style); 

where handle is the workstation handle, and style is a fill style that may be 
any of the following: 


Style Value 

HOLLOW 0 

SOLID I 

PATTERN 2 

HATCH 3 

UDFILLSTYLE 4 


where UDFILLSTYLE signifies that a user-defined fill style will be used. 
These constant values are also defined in the gemlib.h file. 

After setting the interior fill style, the local routines draw_x(), 
draw_c»rcle(), draw_empty_rec(), and dra w„ filled _rec() are called to 
draw the graphics screen. Then, the graphics writing color is set to black 
with the vst_color function whose format is: 

vst_color(handle,color}; 


168 


where handle is the workstation handle, and color is one of the values as 
defined above for vsl_color. The graphics text function, v_gtext, is then 


Graphics 


invoked to display the message PRESS RETURN TO SEE TEXT VARI¬ 
ATIONS . . . ; v-gtext has this format: 

v_gtext(handle,x_coord,y_coord, message); 

where handle is the workstation handle, x_coord and y_coord are the co¬ 
ordinates where the text is to be placed, and message is the actual text. After 
a key is pressed, the screen is cleared via v_clrwk(), and the local function 
do_text() is called to display all the text formats. Then v_gtext is called 
again to inform the user that RETURN must be pressed to quite the program, 
and the usual cleanup routines are executed. 

The draw_x() function is used to draw the large X on the screen. There 
are two local variables declared in draw_x(): num_of_ points and point_set. 
These variables are used in the graphics library routine v^pline whose format 
is: 


v_pline(handle,pt_count,points); 

where handle is the workstation handle, and pt_count is the number of sets 
of points in the points parameter. One shortcoming with v_pline is that it 
cannot draw single points (by passing two identical end points) in its current 
implementation. In draw_x(), num_of_points is set to 2 because there are 
two end points for each line that makes up the X. The routine vsl_color is 
then called to set the drawing color to black. Next, the point.set array is set 
up to hold the end points of the descending (\) line of the X with coordinates 
(50,50) and (125,150). The routine v_pline is then invoked to draw the line, 
the point_set is set to hold the coordinates of the ascending (/) line in the 
X, and v_pline is called again to draw this line. 

The draw_circle( ) function contains declarations for the center coor¬ 
dinates (x_coord,y_coord) and radius of the circle. The coordinates and ra¬ 
dius are set to be (350,100) and 50, respectively, and v-circle is called to 
draw the circle. The v-circle library routine uses these parameters in this 
fashion: 

v_circ!e(handle,x,y,radius); 

The next two routines, draw_empty_rect( ) and draw_filled_rec(), 
operate in similar manners whereby an array (spec_array) is set up to contain 
the coordinates of the opposite comers of the rectangles, and the appropriate 
GEM routine is called to draw the rectangles. In the case of 


169 



Learning C on the ST 


draw_empty_rect(), the opposite comers are (50,175) and (250,325). The 
routine v_bar is called to draw the empty rectangle with the following for¬ 
mat: 


v_bar(handle,coord_array); 

For the filled rectangle in draw_filled_rec(), the interior style must first be 
set up via vsf_interior, where the PATTERN parameter specifies that a pat¬ 
tern will be designated in the call to vsf_style. When vsf_style is then called, 
the GRID parameter informs GEM that we wish to fill with a GRID pattern. 
The spec_array is then filled to hold the opposite comers (300,175) and 
(500,325), and vr_recfl is called to draw the rectangle. So, the sequence of 
events to draw a filled rectangle is first to set up the pattern with both 
vsf_interior and vsf_style, set the opposite comers, and finally invoke vr_recfl 
with this format: 

vr_recfl(handle,coord_array); 

Finally, the do_text( ) routine displays all the graphics text variations 
through the use of vst_gtext (described above) and vst_effects. The style is 
set in vst_effects like this: 

vst_effects(handle,style); 

where style may be one of the following values as defined in the gemlib.h 
file: 


Style 

Value 

THICKENED 

0x0001 

SHADED 

0x0002 

SKEWED 

0x0004 

UNDERLINED 

0x0008 

OUTLINE 

0x0010 

SHADOW 

0x0020 


By passing a zero to vst_effects, all the special effects are turned off (as 
shown at the end of do_text()). [NOTE: In the case of these and all other 
#defined constants, some compilers do not have header files with these val¬ 
ues defined, whereas others do; depending on which compiler you are using, 


170 



Graphics 


you may either have to define them in your source code file or create a header 
file with them in it. J 

These are only some of the graphics functions available to you in the 
GEM library, but as you can see, quite a few interesting graphics applications 
may be developed using the routines described above. Let’s now look at a 
nifty program that uses v_pline and a mouse-locating routine that allows you 
to draw lines on the ST in high resolution. 

Fun with the Mouse -1 


The Atari ST’s mouse can be used in several different ways in C. Here we 
will present one useful routine that allows you to know the status of the 
mouse’s buttons and the position of the cursor on the screen. 

If you enjoy drawing on your ST (with either a TV or color monitor), 
you'll appreciate the following mouse-based program, which allows you to 
draw lines in high resolution. When you run this program, press the left mouse 
button and release it where you want one end point, and move and click it 
again where you want the other end point of a line (remember that v_pline 
cannot draw single points, so if you click twice on the same point, nothing 
will be drawn): 

/*Mouse Drawing Program*/ 

#include (stdio.h) 

#include (portab.h) 

#include (gembind.h) 

int work_stn_hndle,handle; 

int conrtll 12], intin[128], ptsin[128], intout[128l, ptsout!128]; 
main{ j* 

{ 

int nothing; 

window_set_up(); 
graf_mouse{M_ON,&nothing); 

graf_mouse{THIN_CROSS,&nothing);/*make mouse image thin 
cross hair*/ 
mouse_draw(); 
clean_up(); 

} 

window_set_up{) 

{ 


171 





Learning C on the ST 


short loop; 
int work_in[11]; 
int work_out[57]; 
int dummy; 

appl_init(); 

handle = graf_handle(&dummy,&dumrny,&dummy,&dummy); 
work_stn_hndle = handle; 

for(loop = 0; loop < 10; loop + + )work_in[loop] = 1; 
work_in(10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 

appLexitO; 

} 

mouse_draw() 

{ 

int done; /*are we finished yet?*/ 

int point_array[4]; /*hold points for line*/ 
int which_pr; /*which button was pressed?*/ 
int x_pos,y_pos; /*mouse position*/ 
int was_hit; /*flag to tell if button was hit*/ 

done = FALSE; 

vsl_color{work_stn_hndle,BLACK); /*set drawing color to black*/ 
do 

{ 

was_hit = FALSE; 
do 
{ 

if <!was_hit) 

{ 

do 

vq_mouse(work_stn_hndle,&which_pr,&x_pos,&y_pos); 
while (which_pr — 0); 
was_hit - TRUE; 

} 

vq_mouse(work_stn_hndle,&which_pr,&x_pos,&y_pos); 

} while (which_pr!= 0) 

if <(x_pos == 0) && {y_pos == 0)) /*quit if pressed at origin*/ 
done = FALSE; 


172 



Graphics 


else /*draw line*/ 

{ 

point_array[0) = x_pos; 
point_array[1] = y_pos; 
was_hit = FALSE; 
do 

{ 

if (!was_hit) 

{ 

do 

vq_mouse(work_stn_hndle,&which_pr,&x_pos,&y_pos); 
while (which_pr == 0); 
was_hit = TRUE; 

} 

vq_mouse{work_stn_hndle,&which_pr,&x_pos,&y_pos); 

} while (which_pr != 0); 
point_array(2] = x_pos; 
point_array[31 = y_pos; 
v_pline(work_stn_hndle,2,point_array); 

} 

} 

} 

The mouse_draw( ) function is the routine that controls where the lines 
will be drawn on the screen. It uses another GEM library function, vq_mouse, 
whose format is: 

vq_mouse(handle,&which_button,&x,&y); 

where which_button represents which button was pressed. The value re¬ 
turned in which.button is 0 if no button was pressed, 1 if the left button 
was pressed, or 2 if the right button was pressed. The values x and y represent 
the coordinates of the mouse when it was polled. The concept of polling may 
be new to you. but it is very simple. Every time the vq_mouse routine is 
invoked, it is not looking for any particular state (e.g., button down), rather 
it is simply returning the position of the mouse and reporting whether either 
button was pressed. The first incidence of the vq_mouse routine is involved 
in a polling loop. The “do . . . while'’ loop keeps calling vq_mouse until 
which_pr is not equal to zero. In other words, the program remains in an 
infinite loop until a button is pressed on the mouse. For this reason, we say 
that the mouse buttons are being polled and will continue to be polled until 
a button is pressed. 


173 


Learning C on the ST 


The vq_mouse routine executes very quickly, and because of this, there 
has to be a check in the program to determine when the mouse button is 
released after it has been pressed. Otherwise, the possibility exists that before 
the button is released the first time, the next call to vq_mouse may have 
already occurred and the same point would be returned (recall that v_pline 
will not draw single points!). So, the next call to vq_mouse will determine 
if the mouse button has been released yet. If it has not been released, the 
next outer “do . . . while” loop will continue to execute until it is released. 
Notice that the inner “do . . . while” loop will not be executed at this point 
because the was_hit variable was set to TRUE after the button was pressed. 
At this point, the position of the first button press is analyzed, and it the 
coordinates are the origin of the screen (position (0,0), the upper left-hand 
comer of the screen), done is set to TRUE so that the program will be stopped. 
So when you want to stop the program, just click in the upper left-hand comer 
of the screen when you would normally click tor the first point ot the line. 

If the first click is somewhere other than the origin, the mouse position 
will be saved off in the first two elements of the point.array, and a nested 
set of “do . . . while” loops identical to the set described above are exe¬ 
cuted. After these loops return the second click position, the x and y coor¬ 
dinates are placed in the last two positions of the point_array, and v_pline 
is called to draw the line. 

There are many other GEM graphics routines available to you that would 
require an entire book to explain fully. They are fully documented in the 
GEM Programmer's Reference Guide from Abacus Software. At this point, 
you should be able to create your own drawings within C by applying all the 
graphics concepts discussed here. 

Glossary for Review 

polling —the act of repeatedly checking a device to see if any action is 
required within the program to reflect the status returned from the device. 


Quiz 


) 


1. What would happen in the graphics example program if the last call to 
vst_effects in the do_text() function is deleted? 

2. What happens if the mouse is clicked at the origin for the second point 
in the line in the mouse drawing program? 


A Few Programs for the Road 


In this chapter, you will learn: 

• A very useful electronic calendar program that can be modified 
quite easily by incorporating additional routines. 

• A program that may be used to calculate and maintain baseball 
batting averages. 

• A data base program that may be used to catalog your album 
collection electronically. 

• The concept of modularity for designing menu-driven programs. 


j 





Learning C on the ST _ 

The Date Minder Program 




) 


The first program we’d like to present to you in this chapter is one that we 
hope you will find helpful all year round. It’s an electronic calendar that will 
tell you what events are coming up in the next 30 days. All you have to do 
is enter the information for your household (birthdays, anniversaries, etc.) so 
that when you need to see what’s on the horizon for the next month, you 
simply select the “View the next month of events” option from the menu. 
All your calendar information will be stored in a file on your disk so that 
you can add more dates to the file at any time. 

Let’s look at the program and discuss it in detail: 


/•Date Minder Program*/ 

#include (stdio.h) 

#include (gembind.h) 

#define NAME_OF_FILE "DATES" 

struct event_rec /*record for each event*/ 


int month; /*event month*/ 

int date; /*event date*/ 

char occasioned; /*event name*/ 

}; 

int work_stn_hndle,handle; 

int days_in_month[12J = {31,28,31,30,31,30,31,31,30,31,30,31}; 
charmonths [15] (12) = {"January","February","March","April","May'', 

"June","July","August","September", 
"October'V'November'Y'December”}; 
FILE *event_file; /*file of events*/ 

struct event_rec event_var_rec; /*reads/writes event_recs*/ 
int contrl[12], intin[128], ptsin[128], intoutjl281, ptsout[128]; 

main() 

{ 

int nothing; 

window_set_up() 
graf_mouse{M_OFF,&nothing); 
dispfay_menu(); 
graf_mouse{M_ON,&nothing); 


176 


A Few Programs for the Road 


clean_up(); 

} 

window_set_up() 

{ 

short loop; 
int work_in[11]; 
int work_out[57]; 

Int dummy; 

appl_init( ); 

handle - graf-handlel&dummy^dummy^dummy^dummy); 
work_stn_hndle - handle; 

for (loop = 0; loop < 10; loop+ + ) work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 

appl_exit(); 

} 

add_dates() 

{ 

int valid; /*is user's input valid?*/ 

int finished; /*is user finished adding dates?*/ 

char response; /*user's Y/N response for more additions*/ 

event_file = fopen(NAME_OF_FILE/'a"); 
finsished = FALSE; 
while ((finished) 

{ 

valid = FALSE; 
while (Ivalid) 

{ 

printf("\n\nWhat month is this occasion(1-12)? "); 
scanf("%d”,&event_var_rec.month); 

if ({event_var_rec.month > 0) && (event_var_rec.month < 13}) 
valid = TRUE; 

} 

valid = FALSE; 
while (Ivalid) 

{ 


177 


Learning C on the ST 


printf("What date is this occasion (1-31)? "); 
scanf("%d",&event_var_rec.date); 

if {(event_var_rec.date > 0) && (event_var_rec.date <= 
days_in_month[event_var_rec.month - 1])) 

event_var_rec.date += 40; /*avoid control characters*/ 
valid = TRUE; 

} 

} 

printf("What is the occasionAn"); 
gets(event_var_rec.occasion); 

fwrite(&event_var_rec,sizeof(event_var_rec),1,event_file); 

printf("\n\nDo you have any more entries(Y/N)? "); 

response = getchei); 

if ((response == 'N')||(response == 'n')) 

finished = TRUE; 

} 

fclose(event-file); 

printf("\n\n"); 

} 

view„dates(} 

int this_month; /*the month entered by the user*/ 
in this_date; /*the date entered by the user*/ 
int next_month; /*next month (for Jan. after Dec.)*/ 
int valid; 

valid = FALSE; 
while (Ivalid) 

{ 

printf("\n\nWhat is this month (1-12)? "); 
scanf(''%d'',&this_month); 
if ((this_month > 0) && (this_month < 13)) 
valid = TRUE; 

} 

valid = FALSE; 
while (Ivalid) 

} 

printf(What is today's date (1-31)? "); 
scanf("%d'' / &this_date); 

if ((this_date > 0) && (this_date <= days_in_month[this_month - 
1 ])) 


valid = TRUE; 


Few Programs for the Road 


} 

printf("\n****************************************")- 
printf{"******** ************************ ********\ n ")- 
if ((event.file = fopen(NAME_OF_FILE,"r")) != NULL) 

{ 

if (this_month == 12) 
next_month = 1; 
else 

next_month = this_month + 1; 

while (fread(&event_var_rec,sizeof(event-var_rec), 1 ,event_file) 

{ 

event_var_rec.date -=40; /*avoid control characters*/ 
if (((event.var_rec.month == this_mont) && 
(event_var_rec.date >= this_date))|| 
((event_var_rec.month == next_month) && 
(event-var-rec.date < this-date))) 
printf(" %s is on %s %d\n",event_var__rec.occasion, 
months[event_var-rec.month - 1), 
event_var_rec.date); 

} 

printf("\n****************************************")« 
printf("*** ******************************** # ****”\ n ")- 
fclose(event_file); 

} 

else 

printf("\n\n THERE IS NO DATES FILE ON YOUR DISK!\n"); 
printf("\n\nPRESS RETURN TO CONTINUE . . ."); 
getcharO; 
printf("\n\n''); 

} 

display_menu() 

{ 

char selection; 
int valid; 
int done; 

done = FALSE; 
while (Idone) 

{ 

valid = FALSE; 
while (Ivalid) 

{ _ 

printf(”Which would you like to do:\n\n"); 


Learning C on the ST 


printf(" 1. Add dates to the file\n”); 
printf(" 2. View the next month of events\n"); 
printf(" 3. QUIT\n\n”); 
printff'Enter 1, 2, or 3. . ."); 
switch (selection = getcharO) 

{ 

case T:add_dates(); 

valid = TRUE; 
break; 

case '2';view_dates(); 
valid = TRUE; 
break; 

case 'S'lvalid = TRUE; 
done = TRUE; 
break; 

def au It: printf("\n\n\n"); 

} 

} 

} 

} 

The program has a structure type called event_rec, which is used to 
hold the month, date, and name of each occasion. There are also two arrays 
that show how to initialize an array in the declaration; all you need to do is 
place all the initial values in order within a set of braces and precede the 
block with an equal sign. In addition, the months array is a doubly dimen¬ 
sioned array, which means that there are two indices. The first index ot months 
refers to the character position of each entry, whereas the second index relers 
to the number of string entries. In short, the array may hold up to 12 elements 
of up to 15 characters each. And again, this array is initialized by placing 
all the initial values in order (since they are strings, they must be placed 
within quotes) within a set of braces. 

The main routine simply performs initialization, calls display_menu(), 
and performs the cleanup. The routine display—menu( ) shows the main menu 
and, depending on the user's selection, either calls add_dates(), view_dates(), 
or quits the program. 

The add_dates() routine is the one that actually writes the event records 
out to the file. The file where these dates are stored is called DATES. This 
name could be changed by simply changing the #define directive at the be¬ 
ginning of the program. At the start of add„dates(), the file is opened with 
the append option. Then, while the user is not finished adding records/events. 


A Few Programs for the Road 


two loops are performed to validate the input for the month and date of the 
occasion. The occasion name is then entered, and the record is written to the 
file. The user is then asked if he/she has any more entries. If there are more, 
the while (Ifinished) loop is continued; otherwise the file is closed, and con¬ 
trol is passed back to the display_menu() routine. 

The last function in our date minder program is called view_dates(). 
First of all, the current date must be entered by the user. The event file is 
then opened, and we determine the next month using this “if” statement: 

if (this_month == 12) then 
next_month = 1; 

else 

next_month := this_month + 1; 

As you can see, if this month is December, we want to make next month 
January. Otherwise, we just need to add 1 to the current month. 

Then, the events that are within the next month are displayed on the 
screen. A very long “if” statement is used to determine whether a month fits 
into this category. It may be helpful to split this statement into two separate 
statements, either of which may be true for the date and event to be displayed: 

1) if {(event_var_rec.month == this_month)&& 

(event_var_rec.date >= this_date)) 

or: 

2) ({event_var_rec.month == next_month)&& 

(event.var_rec.date < this_date)) 

An example of the first case would be where today is July 10th and the 
event takes place on July 31st; both segments of the case are true, since the 
event’s month (event-var_rec.month) is the same as this_month, and the 
event's date (event-var_rec.date) is greater than this_date. 

An example of the second case would be where, again, today is July 
10th and the event is on August 5th. Both segments are true here also, since 
the event’s month (event-var_rec.month) is the same as next_month and 
the event’s date (event-var_rec.date) is less than this_date. 

When the end of the file is reached, the file is closed, and control is 
returned to the display_menu() routine. 

One other point worth noting in the date minder program is where 40 


181 


Learning C on the ST 


is added to the date before writing it to disk, and then it is subtracted im¬ 
mediately after reading it in. The reason for this is so that control characters, 
or characters that would not be interpreted as the number we intend them to 
be, would be written to the disk and may not be correctly read in. 

Now let's take a look at a program that will allow you to maintain a 
file of your favorite baseball player's batting average. 


Batting Average Program 


The following program allows you to enter up to 20 baseball players, their 
number, number of at bats, and number of hits, and it will calculate their 
batting averages and store the file on disk: 


/"Baseball Averages Program*/ 

#include (stdio.h) 

#include (gembind.h) 

#define TEAM ''BALLCLUB" 

struct player_rec 


{ 

char number[4]; 
char last_name[15l; 
char first_namel10] 
float at_bats; 
float hits; 
float batting_ave; 

}; 


/"player’s number*/ 
/*player's last name*/ 
/"player's first name*/ 
/*number of at bats*/ 
/"number of hits*/ 
/"batting average*/ 


int work_stn_hndle,handle; 

FILE *bb_team_file; /"the team's file*/ 

struct player_rec a_player; /*a player variable*/ 

struct player_rec arr_of_pl[20]; /"array of players*/ 

int num_of_players; /"current number of players*/ 

int contrl[12], lntin[128], ptsin[128], intout[128], ptsout[128]; 


182 


main() 

{ 

int nothing; 



A Few Programs for the Road 


window_set_up(); 
graf_mouse(M_OFF,&nothing); 
disp_menu( ); 

graf_mouse{M_ON,&nothing); 
clean_up(); 

} 

window_set_up(} 

{ 

short loop; 
int work_in[11]; 
int work_out[57]; 
int dummy; 

appl_init(); 

handle = graf_handle{&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle = handle; 

for {loop = 0; loop < 10; loop+ + ) work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 
appl_exit(); 

} 

disp_menu(} 

{ 

int choice; /^user's menu selection*/ 
int done; 
done = FALSE; 
while (Idone) 

{ 

printf{"\n\n\nWhich of the following do you wish to 
perform :\n\n"); 

printf{" 1. Add a player to the file\n"); 
printfl" 2. Delete a player from the file\n">; 
printf{" 3. Enter information for a player\n"); 
printf{" 4. Display the file by batting average\n"); 
printf{" 5. QUIT\n\n">; 
printf{" Please enter your selection; 
switch (choice == getcharf)) 


183 


Learning C on the ST 


{ 

case T:add_plyr(); 
break; 

case '2':del_plyr(); 
break; 

case '3':ent_info(); 
break; 

case '4':do_disp{); 
break; 

case '5':done - TRUE; 
break; 

default:printf("\n\nlNVALID SELECTION. . .TRY AGAIN!\n"); 

} 

} 


ent_info() 

i 

int i; 

char response; 
float data; 

load_array( >; 

for {i = 0; i < num_of_plyrs; i + + ) 

{ 

printf("\n\nDo you have any data for %s (Y/N)* ", 

arr_of_pl[i}.last_name}; 

response = getcharO; 

if ((response == 'Y')||(response == 'y')) 

{ 

printf("\nHow many additional at bats? "); 

scanf("%f",%data); 

arr_of_pl(i].at_bats += data; 

printf("How many additional hits? "); 

scanf("%f",&data); 

arr_of_pl[i].hits += data; 

if (arr_of_pl[i].at_bats != 0.0) 

arr_of_pl[i].batting_ave = arr_of_pl[i].hits / arr_of_pl(i|.at_bats; 
else 

arr_of_pl[i].batting_ave = 0.0; 

} 

} 

re_do_file(); 

} 


184 



A Few Programs for the Road 


del_plyr(} 

{ 

char 1_name[15]; /*last name of player to be deleted*/ 
int i,j; 
int found; 

printf("\n\nWhat is the last name of the player to be deleted? "); 
scanf("%s",&1_name); 
load_array(); 
i = 0; 

found = FALSE; 

while ((i < num_of_p)yrs) && (Ifound)) 
if (strcmp(arr_of_pl[i].last_name,1_name) == 0) 
found = TRUE; 
else 

++i; 

if (Ifound) 

printf("\n\nTHAT NAME IS NOT IN THE FILE!!\n"); 
else 

{ 

for (] = i; j < num_of_plyrs - 1; j+ + ) 
arr_of_pl[jl = arr_of_pl[j + 1]; 

--num_of_plyrs; 
re_do_file{); 

printf("\n\nTHAT PLAYER HAS BEEN DELETED.\n"); 

} 

printf(''PRESS RETURN TO CONTINUE. . 
getchar(); 

} 

do-disp() 

{ 

int i; 
sort(); 

if ((bb_team_file = fopen(TEAM,"r”)) != NULL) 

{ 

printf(''\n\n\nNUMBER NAME AT BATS HITS "); 
printf{"BATTING AVERAGES"); 

printf("-"); 

printf("-An"); 

for (i = 0; i < num_of_plyrs; i+ + ) 

{ 

printf(" %s %-10s %15s ",arr_of_pl[i).number, 

arr_of_pl[i].first_name,arr_of_pl|i].last_name); 


185 






Learning C on the ST 


printf("%4.0f %4.0f .%5.3f\n”,arr_of_pl[i].at_bats, 

arr_of_pl[i].hits,arr_of_pl[i].batting_ave); 

} 

fclose(bb_team_file>; 

} 

else 

printf("\n\nTHERE IS NO BASEBALL FILE ON THIS DISK!!\n"); 
printf(''\n\nPRESS RETURN TO CONTINUE. . 
getchar(); 

} 

add_plyr() 

{ 

load_array(); 

+ + num_of_plyrs; 

printf("\n\nWhat is the player's first name? "); 
scanf(“%s",%arr_of_pl[num_of_plyrs - 1].first_name); 
printf("What is the player's last name? "); 
scanf("%s",&arr_of_pl[num_of_plyrs - 1].last_name); 
printf("What is his number? "); 
scanf("%s'\&arr_of_pl[num_of_plyrs - 1].number); 
printf("How many at bats does he currently have? "); 
scanf("%f'',&arr_of_pl[num_of_plyrs - 1].at_bats); 
printf(“How many hits does he currently have? "); 
scanf("%f'',&arr_of_pl[num_of_plyrs - 11.hits); 
if (arr_of_pl[num_of_plyrs - 1].at_bats != 0.0) 
arr_of_pl[num_of_plyrs - 1].batting_ave = 
arr_of_pl[num_of_plyrs - 1].hits/arr_of_pl(num_of_plyrs - 
1].at_bats; 

else 

arr_of_pl[num_of_plyrs - 1].batting_ave = 0.0; 
re_do_file(); 

} 

re_do_file() 

{ 

int i; 

bb_team_file = fopen(TEAM,"w''); 
for (i = 0; i < num_of_plyrs; i+ + ) 
fwrite(&arr_of_pl[i],sizeof(struct player_rec),1,bb_team_file); 
fclose(bb_team_file); 

} 

sort() 


186 


A Few Programs for the Road 


{ 

int h,j; 

struct player_rec temp_plyr; /^temporary storage for sort( )*/ 

load_array(}; 

for (j = 0; j < num_of_plyrs; j+ + ) 
for(h = 0; j < num_of_plyrs - j; h+ + ) 
if (arr_of_pl[h].batting_ave < arr_of_pl[h + 1].batting_ave) 

{ 

temp_plyr = arr_of_pl[h]; 
arr_of_pl[h] = arr_of_pl[h +1]; 
arr_of_pl(h + 1] = temp_plyr; 

\ 

} 

load_array() 

{ 

num_of_plyrs = 0; 

if ((bb_team_file = fopen(TEAM,"r")) != NULL) 

{ 

while <fread(&arr_of_pl[num_of_plyrs),sizeof(struct player_rec), 
1,bb_team_file) == 1) 

++num_of_plyrs; 

fclose(bb_team_file); 

} 

} 

The baseball averages program introduces no new concepts and should 
therefore be very easy for you to understand. Basically, the menu displays 
four options: add a player, delete a player, enter information for existing 
players, or display the file by order of batting average. The program contains 
a separate function for each of these options, as well as three other routines 
that are used by some or all of the menu option routines. These additional 
functions, re_do_fiIe(), sort(), and load_array(), are responsible for re¬ 
writing the file, sorting the file in the memory array, and reading the file into 
the memory array, respectively. Step through this program and try entering 
a few statistics to verify that the program works as you expect. 

Record Album Data Base Program - 


The final program we will show you is one that allows you to catalog your 
album collection electronically. Once you have entered all your albums, you 


187 


Learning C on the ST 


can then go back and either sequentially view the file or search the file based 
on album title, artist, year of release, or an extra field for your own use. 
Again, there are no new concepts introduced in this program, so you should 
have no problem following the logic: 

/•Album Data Base Program*/ 

#include (stdio.h) 

#include {gembind.h} 

#define ALB_FILE "ALBUMS” 

struct a!bum_type 

{ 

char tltle(25l; /*album title*/ 

char artist[20]; /*album artist*/ 

char year[5|;/*album's year of release*/ 

char xtra_field[20]; /*extra field for your own use*/ 

}; 


int work_stn_hndle,handle; 
struct album_type an_album; 
int done; 

FILE *album_file; 

int contrl[121, intin[128l, ptsin[128], intout[128], ptsout[128]; 

main() 

{ 

int nothing; 
char choice; 

window_set_up(); 
graf_mouse(M_OFF,&nothing); 
done = FALSE; 
while (Idone) 

{ 

v_clrwk(work_stn_hndle); 

v_curhome(work_stn_hndle); 

printf("Select one of the following options:\n\n\n"); 

printf(" 1. Add albums to the file\n"); 

printfi" 2. Sequentially display the fiIe\n"); 

printf(" 3. Search the file\n"); 

printf(" 4. QUIT\n\n\n">; 

printf("What is your choice? "); 

switch (choice = getcharl)) 


188 


A Few Programs for the Road 


{ 

case T:add_albums{); 
break; 

case '2':display_file{); 
break; 

case '3':search( >; 
break; 

case '4':done = TRUE; 
break; 

default: /*don't do anything, screen will be erased*/; 

} 

} 

graf_mouse(M_ON,&nothing); 
clean_up(); 

} 

window_set_up() 

( 

short loop; 
int work_in[11]; 
int work_out(57]; 
int dummy; 

appl_init(}; 

handle = graf_handle{&dummy,&dummy,&dummy,&dummy); 
work_stn_hndle = handle; 

for (loop = 0; loop < 10; loop+ + ) work_in[loop] = 1; 
work_in[10] = 2; 

v_opnvwk(work_in,&work_stn_hndle,work_out); 

} 

clean_up() 

{ 

v_clsvwk(work_stn_hndle); 

appLexitf); 

} 

search() 

{ 

int done; 
char selection; 

donw = FALSE; 
while (Idone) 

{ 


189 



Learning C on the ST 


v_clrwk{work_stn_hndie); 

v_curhome(work_stn_hndie>; 

printf("Which field would you like to search?\n\n\n''); 
printf(" 1. Group/Artist Name\n"); 
printf(" 2. Album Title\n"); 
printf{" 3. Year of Release\n"); 
printf(" 4. Extra field\n''); 
printf{" 5. QUIT\n\n\n"}; 
printfi'Tlease enter your selection: "); 
switch (selection = getchar(» 

{ 

case T:grp_search(); 
break; 

case '2':title_search(); 
break; 

case '3':year_search( ); 
break; 

case '4':tag1_search(); 
break; 

case '5':done = TRUE; 
break; 

default:/*nothing, screen will be re-displayed*/ 

} 

} 

} 

add_albums() 

{ 

int done; 

char *stop = "$"; 
done = FALSE; 

album_file = fopen(ALB_FILE,"a"); 
while (Idone) 

{ 

v_clrwk(work_stn_hndle); 

v_curhome(work_stn_hndle); 

printf("What is the album's titls {$ to end)? "); 

gets(an_album.title); 

if (strcmp(an_album.title,stop) == 0) /*enter a dollar sign to end*/ 
done = TRUE; 
else 

{ 

printf("Who is the artist? "): 


190 



A Few Programs for the Road 


gets(an_album.artist); 

printf("What year was the album made? "); 
gets(an_album.year); 

printf("What do you want in the extra field? "); 
gets(an_album.xtra_field); 

fwrite(&an_album,sizeof{struct album_type),1,album_file); 

} 

} 

fclose<album_file); 

} 

display_.fi le() 

{ 

if {{album_file = fopen(ALB_FILE,"r")) != NULL) 
while^fread(&an_album,sizeof(structalbum_type),1,album_file)==1) 
disp_the_record{); 

else 

{ 

printf<"\n\nTHERE IS NO ALBUM FILE ON THE DISK!\n\n\n"); 

printf{''PRESS RETURN TO CONTINUE. . ."); 

getcharO; 

} 

I 


disp_the_record() 

{ 

v_clrwk(work_stn_hndle); 
v_curhome(work_stn_hndle); 
printfL'The album title is: 
printf("The album artist is: 
printf("The album's release was in 
printf("The extra field is: 


%s\n",an_album. title); 
%s\n'',an_album.artist); 
%s\n",an_album.year); 
%s\n\n\n\n", 
an_album.xtra_field); 


printfCTRESS RETURN TO CONTINUE. . ."); 
getchar(); 

} 


grp_search() 

{ 

int found_one; /*have we found a match yet?*/ 

char group[20]; /*holds name of group we wish to search*/ 


v_clrwk{work_stn_hndle); 

v_curhome(wor_stn^hndle); 


191 


Learning C on the ST 


printf("Which Group/Artist do you wish to search? 
gets(group); 

if {(album_file = fopen(ALB_FILE,"r")) != NULL) 

{ 

found_one = FALSE; 

while (fread{&an_album,sizeof(structalbum_type),1,album_file)==1) 
if <strcmp(an_album.artist,group) == 0) 

{ 

found_one = TRUE; 
disp_the_record(); 

} 

if (!found_one) 

{ 

printf("\n\nTHAT ARTIST IS NOT IN THE FILE!\n\n"); 
printfC'PRESS RETURN TO CONTINUE . . 
getchar( }; 

} 

fclose(album_file); 

} 

else 

{ 

printf("\n\nTHERE IS NO ALBUM FILE ON THE DISK!\n\n\n”}; 

printf("PRESS RETURN TO CONTINUE. . 

getchar(); 

} 

} 

tag1_search() 

{ 

int found_one; 
char tag 1 [20); 

v_clrwMwork_stn_hndle); 

v_curhome(work_stn_hndle); 

printf("What is the extra field you wish to search? "); 
gets(tagl); 

if ((album_file = fopen(ALB_FILE/'r'')) != NULL) 

{ 

found_one = FALSE; 

while (fread{&an_al bum,sizeof(structalbum_type),1,album_file)==1) 
if (strcmp(an_album.xtra_field,tag1) == 0) 

{ 

found_one = TRUE; 
disp_the_record(); 


192 


A Few Programs for the Road 


} 

if (!found_one) 

{ 

printf("\n\nTHAT EXTRA FIELD IS NOT IN THE FILE!\n\n"}; 
printf("PRESS RETURN TO CONTINUE. . 
getchar( }; 

} 

fclose{album_file); 

} 

else 

{ 

printf("\n\nTHERE IS NO ALBUM FILE ON THE DISK!\n\n\n"); 
printf("PRESS RETURN TO CONTINUE. . 
getchar( ); 

} 

} 

title_search() 

{ 

int found_one; 
char title[25|; 

v_clrwk(work_stn_hndle); 
v_curhome(work_stn_hndle}; 
printf{"What titles do you wish to search? "); 
gets(title); 

if ((album_file = fopen(ALB_FILE,"r")) != NULL) 

{ 

found_one = FALSE; 

while (fread(&an_al bum, sizeof{structalbum_type),1,album_file)==1) 
if (strcmp{an_album.title,title) == 0} 

{ 

found_one = TRUE; 
disp_the-record(); 

} 

if (Ifound) 

{ 

printf("\n\nTHAT TITLE IS NOT IN THE FILE!\n\n”); 
printf("PRESS RETURN TO CONTINUE. . ."); 
getchar{); 

} 

fclose(album_file); 

} 

else 


193 



Learning C on the ST 


{ 

printf("\n\nTHERE IS NO ALBUM FILE ON THE DISK!\n\n\n"); 
printf("PRESS RETURN TO CONTINUE. . 
getchar{); 

} 

} 

year_search() 

{ 

int found_one; 
char year[5]; 

v_clrwk(work_stn_hndle); 
v_curhome|work_stn_hndle); 
printf("What year do you wish to search? "); 
gets(year); 

if ((album_file = fopen(ALB_FILE,"r")) != NULL) 

{ 

found_one = FALSE; 

while (fread(&an_album,sizeof(structalbum_type),1,album_file}==1) 
if (strcmp(an_album.year,year) == 0) 

found_one = TRUE; 
disp_the_record{); 

} 

if (!found_one) 

{ 

printf("\n\nTHAT YEAR IS NOT IN THE FILE!\n\n"); 
printf("PRESS RETURN TO CONTINUE. . 
getchar(); 

} 

fclose(album_file); 

} 

else 

I 

printf("\n\nTHERE IS NO ALBUM FILE ON THE DISK!\n\n\n"); 
printf("PRESS RETURN TO CONTINUE. . ."); 
getcharl); 

} 

} 

The album data base program is very modularized and lends itself to the 
addition of new options and modules. Each of the search routines is almost 
identical, except, of course, for the field being searched. When you wish to 
stop adding albums to the file, just enter a dollar sign ($) and control will 


194 


A Few Programs for the Road 


return to the main menu. The extra field in the structure may be used to 
specify the condition of the album, the owner of the album, etc. Be sure to 
log some or all of your albums so that you can see how easy it is, then to 
see if you have any albums by a particular artist, from a certain year, etc. 

Summing Up -1 


The date minder program can definitely be a help to you and your family if 
you are prone to forgetting important dates. The program is also set up in a 
modular manner so that you could easily add more options to the main menu— 
like viewing the next 60 days' events or maybe even the entire file. Because 
of the modular structure of the program (i.e., each task has its own separate 
routine), all you would need to do is add more options to the menu in 
display_menu () (and make sure they are accepted as valid in the “if” and 
switch statements thereafter) and add the appropriate functions to the pro¬ 
gram. The baseball and album programs are both quite modular and can there¬ 
fore easily be customized to your own unique needs. 

We sincerely hope that the information you have learned in this and all 
the preceding chapters are all you need to write your own applications. Even 
though you are now finished with this book, you should always keep it handy 
when working in C on your Atari ST as a quick reference guide. 

Glossary for Review 

modular structure —the structure used in all the programs in this chap¬ 
ter, where each separate task has its own routine; more tasks can be added 
by simply developing additional routines 

multidimensional arrays —arrays that have two or more indices (such 
as the months variable in the date minder program) 

Quiz -> 


1. Why couldn’t we simply have added 1 to the current month to get the 
value of the next month in the date minder program? 

2. Why do we always check the value of the at_bats field before calculating 
the batting average in the baseball averages program? 

3. How are invalid entries handled in the main menu of the album data base 
program? 


195 





Appendix A 


Reserved Words 


auto 

break 

case 

char 

continue 

default 

do 

double 

else 

entry 

extern 

float 

for 

goto 

if 

int 

long 

register 

return 

short 

sizeof 

static 

struct 

switch 

typedef 

union 

unsigned 

while 





Appendix B 


Answers to Quiz Questions 


Chapter 1 

1. This compiler follows the rules of C as laid out in Kemighan and 
Ritchie’s The C Programming Language (1978, Prentice-Hall). 

2. Power and portability. 

3. This statement is a comment and is therefore ignored by the com¬ 
piler, even though the printf command is misspelled. 

Chapter 2 

1. Generally, any identifier created with a #define statement should be 
specified in lowercase letters. 

2. #include (filename. ext) and #include “filename.ext”. 

3. Both data types occupy 4 bytes of memory. 

4. ‘\0’ is the string terminator. 

Chapter 3 

1 . The ampersand (&) is missing before the num parameter. 

2. getcharQ may be used to get input from the user, and putchar() 
may be used to display the input from getchar(). 


Learning C on the ST 


3. If the num variable is not zero, the first message is printed; other¬ 
wise, the second is printed. 

Chapter 4 

1. The “do-while” loop will always execute at least one iteration, whereas 
the “while” loop may not execute any if the condition is not true. 

2. num++ increments num after its current value (post-incrementing) 
has been used in this statement, and ++num increments num (pre¬ 
incrementing), and this new value is used in the statement. 

3. The loop will not execute any iterations, since i is initialized to 5, 
and the condition for looping states that it must be less than 5 to 
perform another iteration. 

Chapter 5 

1. Global variables may be accessed and changed by any function, 
whereas local variables are limited to change by only the function in 
which they are declared. 

2. This declares number to be a pointer to an int value. 

3. Passing by address permits the called function to modify the actual 
parameters that the calling function sent. Passing by value, on the 
other hand, does not allow this, since a copy, and not the actual 
value, is passed to the routine. 

Chapter 6 

1. A. Atari B. 5 

C. 12 D. nonzero 

E. zero F. zero 

2. Any test sent to the screen via printf will begin where the cursor 

was positioned before the call to v_clrwk. 

3. The parameters in wind_create represent the maximum size of the 
window, whereas they represent the actual initial size in wind_open. 

Chapter 7 

1. Because they vary throughout the program depending on the number 
entered. 



Answers to Quiz Questions 


2. They are broken apart so that they may be called independently of 
each other (e.g., what if you only wanted to do one conversion?). 

3. Because you should always have at least one entry. 

4. Yes. All the numbers from zero through nine. 

Chapter 8 

1. Simply put, a file is a collection of records, and a record is a group¬ 
ing of fields! 

2. Because the purpose of a record/structure is not to try and centralize 
all your data structures, but rather to group together all related items 
(like the name, position, etc., in our baseball program). 

3. It was used to remove any possible “garbage” that may have been 
left over in memory from any other variables that may have been 
using that memory location. It is not necessary for you to understand 
why this “garbage” might exist; however, it is important for you 
always to initialize your arrays just as you would initialize any other 
variable. 

Chapter 9 

1. The entire collection would be called the “file,” each album would 
be called a “record” or “entry,” and each song would be a “field” 
for that particular “record.” 

2. We used the append (“a”) mode for writing to the file in the fopen 
function. 

3. You could use a constant name as declared with #define; this would 
limit your capability to have more than one file, however, since you 
would have to change the #define statement and recompile and re¬ 
link every time you wanted to use a different file name. 


Chapter 10 

1. It forces the programmer to think through the program logic more 
thoroughly without having a debugger to fall back on for help. 

2. C IS FUN 


201 


Learning C on the ST 


Chapter 11 

1. The SHADOW effect would still be in effect, and, therefore, the text 
displayed in the v.gtext after do_text() would be displayed with the 
shadowed effect. 

2. If the mouse is clicked at the origin on the second point, a line is 
drawn from the first point to the origin. Remember that the only time 
a click at the origin causes the program to stop is when it is the first 
of the two clicks. 

Chapter 12 

1. That would be fine and is indeed what we do, except for when the 
current month is December, and the next month is therefore January; 
adding 1 to 12 would be 13, not 1! 

2. If we didn’t check the value of the at_bats field, it would be possible 
to try to divide by zero, which would cause the program to abort. 

3. The default option in the switch statement performs nothing, so when 
the “while” loop starts again, the screen is simply redisplayed, and 
the menu once again prompts for a selection. 


Index 



Accumulator loop, 55 
Advanced data structures/concepts 
arrays, 121-30 
parallel, 124-25 
simple, 121-24 
numeric types, 120-21 
recursion, 130-33 
structures/files, 125-30 
appl_exit(), 92 

Application Environment System 
(AES), 5 
appl_init(), 91 
Arrays 

application use, 127-30 
multidimensional arrays, 195 
parallel arrays, 124-25 
simple arrays, 121-24 
versus files, 137 
Atari ST C compilers, 3-5 
Graphics Environment Manager 
(GEM), 4-5 

Application Environment System 
(AES), 5 

Virtual Device Interface (VDI), 5 

Baseball averages program, 182-87 
Baseball lineup program, 127-30 
BASIC language 
versus C language, 2-3 
interpreter, 3 
origin of, 2 


Binary search, 104 
Bits, definition of, 19-20 
Byte, definition of, 19-20 


Central library. 86-87 
CIPHTEXT file, 160 
C language 
versus BASIC 2-3 
data types, 19-24 
versus Logo, 2-3 
origin of, 2 
programs 

elements of, 16-18 
expressions, 27-28 
mathematical order of operation, 
28-29 

program formatting, 31-32 
simple C arithmetic, 29-31 
storage classes, 25-27 
syntax, 16 
variables, 18-19 
assignment of, 24-25 
Compilation process. Megamax C 
compiler, 7-8 

Conditional statements, 39-49 
“if” statement, 40-42 „ 
common errors with, 42-44 
nesting of, 44-46 
logical operators, 46Logical 
operators, 46 



Index 


?:(conditional) operator, 46-4946 
switch alternative, 47-49 
Cosine function, 87 
C Programming Language 

(Kemighan/Ritchie), 2 

Data types 
characters, 22-23 
floats, 21-22 
identifiers, 24 
int, 19-20 
strings, 23-24 

Date minder program, 176-82 
Debugging 
definition of, 152 
echo printing, 153 
tips, 152-53 

Display phone book program. 148-49 
“Do-while” statement, 65-66 
versus “while” statement, 66 

Echo printing. 153 
Encipher/decipher program, 158-60 
Expressions, 27-28 
External storage class, 26-27 

Family budget program, 65-66 
File analysis 

encipher/decipher program, 158-60 
hex/character file dump program, 
153-56 

Files, 125-30, 136 
CIPHTEXT file, 160 
handling library routines, 138-43 
phone book program and, 139-43 
versus arrays of structures, 137 
Formatting, 31-32 
“For” statement, 59-65 
defintion of, 59 
flexibility of, 63 
Functions 

ASClI-to-integer function, 87-88 
components of, 73 


cosine function, 87 
definition of, 73 
functions program, 74-75 
getc() function, 156 
gets function, 88 
macros, 89-90 

mathematical functions, 87-88 
placement of, 73-74 
return statement, 80-82 
sine function, 87 
square root function, 87 
strcat function, 88-89 
strcmp function, 89 
string functions, 88-89 
strlen function, 89 
uses of, 74-76 
using variables with, 76-79 
global versus local variables, 
76-78 

value parameters, 78-79 
variable parameters, 82-84 

Gas mileage calculation program, 
29-30 

getc() function, 156 
getchar function, 39 
gets function, 88 
global variables, 76-77 
goto statement, 67-68 
graf_handle(), 91 
graf_mouse(), 92-93 
Graphics 

Atari ST graphics, 164-71 
graphics example program, 
164-67 

v_bar routine, 170 
v_circle library routine, 169 
v_gtext function, 168-69 
v_pline library routine, 169 
v_recf function, 170 
vsf_interior function, 168 
vsl_color routine, 167, 168 
vst_effects function, 170 


Index 


mouse drawing program, 176-82 
vq_mouse function, 173-74 
Graphics Environment Manager 
(GEM), 4-5 

Application Environment System 
(AES), 5 

functions of, 90-93 
appl_exit(), 92 
appl_init(), 91 
graf_handle(), 91 
graf_mouse(), 92-93 
v_clrwk(), 93 
v_clswk(), 92 
v_curhome(), 93 
v_opnvwk(), 92 
vq_mouse, 173-74 
windows, 93-97 

Virtual Device Interface (VDI), 5 

Hexadecimal conversion program, 
110-14 

ASCII conversion values, 156-58 
program explanation, 116-17 
Hex/character file dump program, 
153-56 

“If’ statement, 40-42 
common errors with, 42-44 
“if” statement program, 41-43 
nesting of, 44-46 
Interpreters, 2-3 

Lattice C compiler, 3-4 
Library features 
central library, 86-87 
Graphics Environmental Manager 
(GEM) functions, 90-93 
windows, 93-97 
standard C functions, 87-90 
macros, 89-90 
mathematical, 87-88 
string, 88-89 


Linking, Megamax C compiler, 
10-11 

Local variables, 76-77 
Logical operators, 46 
Logo, versus C language, 2-3 
Loop counter variable, construction 
of, 55 

Looping structures 
accumulator loops, 55 
counting loops, 54-55 
“do-while” statement, 65-66 
“for” statement, 59-65 
definition of, 59 
flexibility of, 63 
goto statement, 67-68 
infinite loops, 53 
loops that sum, 55-59 
nested loops, 66-67 
summing loops, 55 
“while” statement, 52-54 


Macros, 89-90 

Mark Williams C package, 3, 4 
Mathematical functions 
ASCII-to-integer function, 87-88 
cosine function, 87 
sine function, 87 
square root function, 87 
Mathematical order of operation, 
28-29 

Megamax C compiler 
compilation process, 7-8 
compiler package, 3, 4 
float data types, 22 
int data types, 21 
use of, 5-12 
linking, 10-11 
microfloppy disks, 5 
program entry, 5-6 
saving programs, 6-7 
Merging, 143-50 
definition of, 150 


205 



Index 


display phone book program, 
148-49 

sort/merge phone book program, 
144-47 

Metric conversions program, 100-4 
program explanation, 103-4 
Microfloppy disks, Megamax C 
compiler, 5 

Modular programming, 72 
Modular structure, 195 
Mouse 

mouse drawing function, 176-82 
vq mouse function, 173-74 
Mouse drawing program, 176-82 
Multidimensional arrays, 195 

Nested loops, 66-67 
conditional statements, 44-46 
nested loop program, 67 
NULL, definition of, 139, 150 
Number guessing program, 104-10 
program explanation, 108-10 
Numeric types, advanced, 120-21 

Parallel arrays, 124-25 
Phone book program, 139-43 
Pixels, 95 
Polling, 173, 174 

Post-/pre-decrementing, 57-59 

Post-/pre-incrementing, 57-59 
Printf function, 34-37 
Program entry, Megamax C compiler, 
5-6 

Program formatting, 31-32 
Programs 

accounting program, 60-61 
with less code, 62, 63-64 
baseball averages program, 182-87 
date minder program, 176-82 
difference between ++var/var+ + 
program, 58 
elements of, 16-18 
family budget program, 65-66 


first “while” loop program, 53 
functions program, 74-75 
gas mileage calculation program, 
29-30 

“if” statement program, 41-43 
metric conversion program, 100-4 
program explanation, 103-4 
nested loop program, 67 
number guessing program, 104-9 
program explanation, 108-9 
pay including overtime program, 
45-46 

phone book program. 139-43 
products in a loop calculation, 54 
record album data base program, 
187-95 

recursion program, 131-32 
student's grade point average 
program, 30-31 
summing program, 55-57 
window display program, 94-95 
Putchar function, 37 

Record album data base program, 
187-95 

Record data structure, 125-30 
Recursion, 130-33 
Recursion Program, 131-32 
Register storage class, 26 
Reserved words, 197 
Return statement, 80-82 

Saving programs, Megamax C 
compiler, 6-7 
scanf function, 37-38 
Scope rules, 76 
Sign bit, 120 
Simple arrays, 121-24 
Simple C arithmetic, 29-31 
Sine function, 87 
Sorting 

definition of, 150 


206 



Index 


display phone book program, 
148-49 

sort merge phone book program. 
144-47 

Square root function, 87 
Statements 

conditional statements, 39-49 
“do-while” statement, 65-66 
“for” statement, 59-64 
getchar function, 39 
goto statement, 67-68 
“if” statement, 40-42 
printf function, 34-37 
putchar function, 37 
scanf function, 37-38 
“while” statement, 52-54 
See also Conditional statements. 
Static storage class, 26 
Stepwise refinement, 72 
Storage classes, 25-27 
for automatic variables, 25-26 
external storage class, 26-27 
register storage class, 26 
static storage class, 26 
Strcat function, 88-89 
Strcmp function, 89 
String functions 
gets function, 88 
strcat function, 88-89 
strcmp function, 89 
strlen function, 89 
strlen function, 89 
Structured design, 72 
Structures, 125-30 
application use, 127-30 
Student’s grade point average 
program, 30-31 
Summing loop, 55 


Summing program. 55-57 
Switch alternative, 47-49 
Symbolic constants, 17 
Syntax, 16 

Top-down design, 72 

Unsigned modifier, 20-21 

Variable parameters, 82-84 
Variables, 18-19 
assignment of, 24-25 
global variables, 76-77 
identifiers, 24 
local variables, 76-77 
used with functions, 76-79 
value parameters, 78-79 
variable parameters, 82-84 
v_bar routine, 170 
v_circle library routine, 169 
v_clrwk(), 93 
v_clsvwk(), 92 
v_curhome(), 93 
v_gtext function, 168-69 
Virtual Device Interface (VDI), 5 
v_opnvwk(), 92 
v_pline library routine, 169 
vq_mouse, 173-74 
v_recf function, 170 
vsf_interior function, 168 
vsI_color routine, 167, 168 
vst_effects function, 170 

“While” statement, 52-54 
versus “do-while” statement, 66 
“while” loop program, 53 
Windows, 93-97 


207 







Here’s how to receive your free catalog and save 
money on your next book order from Scott, 
Foresman and Company. 

Simply mail in the response card below to receive your free copy of our 
latest catalog featuring computer and business books. After you’ve looked 
through the catalog and you’re ready to place your order, attach the cou¬ 
pon below to receive $1.00 off the catalog price of Scott, Foresman and 
Company Professional Publishing Group computer and business books. 


□ YES, please send me my free catalog of your latest computer and business books! 
1 am especially interested in 


□ IBM □ 

□ MACINTOSH □ 

□ AMIGA □ 

□ COMMODORE □ 


Programming 
Business Applications 
Networking/Telecommunications 
Other _ 


Name (please print)--- 

Company ---—-—-- 

Address ------ 

City _ State _ Zip 


Mail response card to: Scott, Foresman and Company 
Professional Publishing Group 
1900 East Lake Avenue 
Glenview, IL 60025 


PUBLISHER’S COUPON NO EXPIRATION DATE 

SAVE $1.00 


Limit one per order. Good only on Scott, Foresman and Company 

Professional Publishing Group publications. Consumer pays any sales tax. Coupon may 
not be assigned, transferred, or reproduced. Coupon will be redeemed by Scott, Foresman 
and Company Professional Publishing Group, 1900 East Lake Avenue. Glenview, IL 60025. 


Customer’s Signature 






You Already Own a Powerful Personal Computer . . . Now Put Its 
Power to Work for You with Learning C on the Atari ST 

If you want to create your own programs in C on the Atari ST personal com¬ 
puter, Learning C on the Atari ST is for you. Beginning and experienced 
programmers alike will appreciate the easy-to-use, readable style of Learning 
C on the Atari ST. Because the book describes the C programming language 
so thoroughly, beginners will be able to write code immediately. Advanced 
programmers will appreciate the detailed coverage of the unique features of 
the ST. 

This well-organized book includes: 

• Recommended C compilers for the Atari ST 

• What the Graphics Environment Manager is, and how you can use 
GEM to take advantage of the ST’s powerful capabilities 

• The proper syntax and structure of a C program, what variables are, how 
to comment and define constants 

• How to write statements a n d routines as well as how to construct a loop 

• Various C functions and how to write more sophisticated programs 
that use files, multi-dimensional arrays, and pointers 

• Popular graphics routines, and how to design programs that use a 
mouse. 

And, all programs are written so that they can be easily converted to most ST 
C compilers on the market. 

Because each chapter begins with objectives and ends with a review and quiz, 
you won’t miss important information essential to a clear understanding of the 
C language. With Learning C on the Atari ST, you’ll build solid C program¬ 
ming skills. And because this guide is so easy to use, you’ll want to keep it 
handy as a reference guide. 

Joseph Boyle Wikert is a systems analyst for NCR Corporation. He is also coauthor of 
Using Your Macintosh: Beginning Microsoft BASIC and Applications and Learning 
Macintosh Pascal (published by Scott, Foresman). He currently lives in Miamisburg, Ohio. 


Scott, Foresman and Company 


ISBN D-b73-lfi73fl-l